knitr::opts_chunk$set(fig.width=22, fig.height=20) 
library(tidyverse)
library(caret)
library(mgcv)


library(visreg)

Import

Importing Cleaned Data

ames = read_csv('./data/train_cleanest.csv')
test = read_csv('./data/test_cleanest.csv')


ames = ames[,order(colnames(ames))]
test= test[,order(colnames(test))]

ames = ames %>% dplyr::select(-X1)
test = test %>% dplyr::select(-X1)

ames = ames[,order(colnames(ames))] %>% 
  rename('FirstFlrSF' = "1stFlrSF", 'SecFlrSF' = '2ndFlrSF', 'ThreeSeaPorch' = '3SsnPorch')


test = test[,order(colnames(test))] %>% 
  rename('FirstFlrSF' = "1stFlrSF", 'SecFlrSF' = '2ndFlrSF', 'ThreeSeaPorch' = '3SsnPorch')

ames$PriceRange <- factor(ames$PriceRange, levels = c("High", "Middle", "Low"))

test$PriceRange <- factor(test$PriceRange, levels = c("High", "Middle", "Low"))

ames$QualCond = ames$OverallCond * ames$OverallQual

test$QualCond = test$OverallCond * test$OverallQual

Split into train and validation

set.seed(3)
train.idx = sample(1:nrow(ames), 8*nrow(ames)/10)
ames_train = ames[train.idx,]
ames_test = ames[-train.idx,]
## Base GAM Model
ames.gam.base <- mgcv::gam(logPrice
                ~ s(TotalSF, by=PriceRange)
                + s(QualCond, by= PriceRange)
                + s(Age)
                + Fireplaces:PriceRange
                + s(MSSubClass)
                + Neighborhood:PriceRange
                + s(GarageArea, by=GarageCars),
                method='GCV.Cp',
                data=ames)

## Base LM Model
ames.lm <- lm(logPrice
              ~ TotalSF:PriceRange
              + QualCond
              + Age
              + Fireplaces:PriceRange
              + MSSubClass
              + Neighborhood:PriceRange
              + GarageArea:GarageCars,
              data=ames)

true = ames_test[,'SalePrice'][[1]]

# gam.predictions = predict.gam(ames.gam, newdata = ames_test, type = 'response')
# gam.errors = gam.predictions - log(true)
# gam.diff =exp(gam.predictions) - true

gam.base.predictions = predict.gam(ames.gam.base, newdata = ames_test, type = 'response') #log preds
gam.base.errors = gam.base.predictions - log(true) # residuals
gam.base.diff =exp(gam.base.predictions) - true # $ diff

lm.predictions = predict(ames.lm, newdata = ames_test)
prediction from a rank-deficient fit may be misleading
lm.errors = lm.predictions - log(true)
lm.diff = exp(lm.predictions) - true

print(paste('GAM RMSE:',sqrt(mean(gam.base.errors^2))))
[1] "GAM RMSE: 0.121294811423439"
print(paste('LM RMSE:',sqrt(mean(lm.errors^2))))
[1] "LM RMSE: 0.130157662484692"
print(paste('GAM: Error in $ as Predicted:',round(mean(abs(gam.base.diff)),2)))
[1] "GAM: Error in $ as Predicted: 14891.7"
print(paste('LM: Error in $ as Predicted:',round(mean(abs(lm.diff)),2)))
[1] "LM: Error in $ as Predicted: 15965.89"
summary(ames.gam.base)

Family: gaussian 
Link function: identity 

Formula:
logPrice ~ s(TotalSF, by = PriceRange) + s(QualCond, by = PriceRange) + 
    s(Age) + Fireplaces:PriceRange + s(MSSubClass) + Neighborhood:PriceRange + 
    s(GarageArea, by = GarageCars)

Parametric coefficients:
                                      Estimate Std. Error t value Pr(>|t|)    
(Intercept)                          11.641766   0.016218 717.809  < 2e-16 ***
Fireplaces:PriceRangeHigh             0.055237   0.012139   4.550 5.83e-06 ***
Fireplaces:PriceRangeMiddle           0.042844   0.008901   4.813 1.65e-06 ***
Fireplaces:PriceRangeLow              0.073847   0.015465   4.775 1.99e-06 ***
PriceRangeHigh:NeighborhoodBlmngtn    0.344139   0.074638   4.611 4.39e-06 ***
PriceRangeMiddle:NeighborhoodBlmngtn  0.240303   0.039344   6.108 1.31e-09 ***
PriceRangeLow:NeighborhoodBlmngtn     0.000000   0.000000      NA       NA    
PriceRangeHigh:NeighborhoodBlueste    0.000000   0.000000      NA       NA    
PriceRangeMiddle:NeighborhoodBlueste  0.201568   0.090975   2.216 0.026880 *  
PriceRangeLow:NeighborhoodBlueste     0.000000   0.000000      NA       NA    
PriceRangeHigh:NeighborhoodBrDale     0.000000   0.000000      NA       NA    
PriceRangeMiddle:NeighborhoodBrDale   0.000000   0.000000      NA       NA    
PriceRangeLow:NeighborhoodBrDale      0.138927   0.042398   3.277 0.001076 ** 
PriceRangeHigh:NeighborhoodBrkSide    0.000000   0.000000      NA       NA    
PriceRangeMiddle:NeighborhoodBrkSide  0.237728   0.031846   7.465 1.48e-13 ***
PriceRangeLow:NeighborhoodBrkSide     0.072044   0.027134   2.655 0.008019 ** 
PriceRangeHigh:NeighborhoodClearCr    0.337947   0.031723  10.653  < 2e-16 ***
PriceRangeMiddle:NeighborhoodClearCr  0.252277   0.044608   5.655 1.89e-08 ***
PriceRangeLow:NeighborhoodClearCr     0.000000   0.000000      NA       NA    
PriceRangeHigh:NeighborhoodCollgCr    0.307020   0.019342  15.873  < 2e-16 ***
PriceRangeMiddle:NeighborhoodCollgCr  0.288363   0.028856   9.993  < 2e-16 ***
PriceRangeLow:NeighborhoodCollgCr     0.210468   0.028391   7.413 2.15e-13 ***
PriceRangeHigh:NeighborhoodCrawfor    0.269848   0.026944  10.015  < 2e-16 ***
PriceRangeMiddle:NeighborhoodCrawfor  0.224607   0.037474   5.994 2.62e-09 ***
PriceRangeLow:NeighborhoodCrawfor     0.000000   0.000000      NA       NA    
PriceRangeHigh:NeighborhoodEdwards    0.412058   0.124327   3.314 0.000943 ***
PriceRangeMiddle:NeighborhoodEdwards  0.179379   0.026754   6.705 2.94e-11 ***
PriceRangeLow:NeighborhoodEdwards     0.137103   0.021285   6.441 1.64e-10 ***
PriceRangeHigh:NeighborhoodGilbert    0.290500   0.025019  11.611  < 2e-16 ***
PriceRangeMiddle:NeighborhoodGilbert  0.245987   0.025719   9.565  < 2e-16 ***
PriceRangeLow:NeighborhoodGilbert     0.000000   0.000000      NA       NA    
PriceRangeHigh:NeighborhoodIDOTRR     0.000000   0.000000      NA       NA    
PriceRangeMiddle:NeighborhoodIDOTRR   0.268432   0.089837   2.988 0.002858 ** 
PriceRangeLow:NeighborhoodIDOTRR     -0.031807   0.027065  -1.175 0.240102    
PriceRangeHigh:NeighborhoodMeadowV    0.000000   0.000000      NA       NA    
PriceRangeMiddle:NeighborhoodMeadowV  0.142333   0.126316   1.127 0.260025    
PriceRangeLow:NeighborhoodMeadowV     0.093731   0.041757   2.245 0.024947 *  
PriceRangeHigh:NeighborhoodMitchel    0.228196   0.049169   4.641 3.80e-06 ***
PriceRangeMiddle:NeighborhoodMitchel  0.211249   0.030170   7.002 3.95e-12 ***
PriceRangeLow:NeighborhoodMitchel     0.179612   0.030395   5.909 4.33e-09 ***
PriceRangeHigh:NeighborhoodNAmes      0.133258   0.037750   3.530 0.000429 ***
PriceRangeMiddle:NeighborhoodNAmes    0.152418   0.015605   9.767  < 2e-16 ***
PriceRangeLow:NeighborhoodNAmes       0.219770   0.031587   6.958 5.35e-12 ***
PriceRangeHigh:NeighborhoodNoRidge    0.331654   0.027797  11.931  < 2e-16 ***
PriceRangeMiddle:NeighborhoodNoRidge  0.000000   0.000000      NA       NA    
PriceRangeLow:NeighborhoodNoRidge     0.000000   0.000000      NA       NA    
PriceRangeHigh:NeighborhoodNPkVill    0.000000   0.000000      NA       NA    
PriceRangeMiddle:NeighborhoodNPkVill  0.209417   0.045940   4.559 5.61e-06 ***
PriceRangeLow:NeighborhoodNPkVill     0.000000   0.000000      NA       NA    
PriceRangeHigh:NeighborhoodNridgHt    0.397193   0.023636  16.805  < 2e-16 ***
PriceRangeMiddle:NeighborhoodNridgHt  0.348314   0.089432   3.895 0.000103 ***
PriceRangeLow:NeighborhoodNridgHt     0.000000   0.000000      NA       NA    
PriceRangeHigh:NeighborhoodNWAmes     0.195002   0.075131   2.595 0.009547 ** 
PriceRangeMiddle:NeighborhoodNWAmes   0.203194   0.019970  10.175  < 2e-16 ***
PriceRangeLow:NeighborhoodNWAmes      0.000000   0.000000      NA       NA    
PriceRangeHigh:NeighborhoodOldTown    0.261913   0.202631   1.293 0.196380    
PriceRangeMiddle:NeighborhoodOldTown  0.070476   0.037437   1.883 0.059975 .  
PriceRangeLow:NeighborhoodOldTown     0.018788   0.019332   0.972 0.331288    
PriceRangeHigh:NeighborhoodSawyer     0.000000   0.000000      NA       NA    
PriceRangeMiddle:NeighborhoodSawyer   0.149423   0.020412   7.321 4.20e-13 ***
PriceRangeLow:NeighborhoodSawyer      0.124801   0.040541   3.078 0.002122 ** 
PriceRangeHigh:NeighborhoodSawyerW    0.290633   0.028635  10.150  < 2e-16 ***
PriceRangeMiddle:NeighborhoodSawyerW  0.229089   0.026704   8.579  < 2e-16 ***
PriceRangeLow:NeighborhoodSawyerW     0.114862   0.058799   1.953 0.050966 .  
PriceRangeHigh:NeighborhoodSomerst    0.349811   0.026017  13.446  < 2e-16 ***
PriceRangeMiddle:NeighborhoodSomerst  0.379811   0.023794  15.962  < 2e-16 ***
PriceRangeLow:NeighborhoodSomerst     0.000000   0.000000      NA       NA    
PriceRangeHigh:NeighborhoodStoneBr    0.462130   0.029674  15.574  < 2e-16 ***
PriceRangeMiddle:NeighborhoodStoneBr  0.000000   0.000000      NA       NA    
PriceRangeLow:NeighborhoodStoneBr     0.000000   0.000000      NA       NA    
PriceRangeHigh:NeighborhoodSWISU      0.000000   0.000000      NA       NA    
PriceRangeMiddle:NeighborhoodSWISU    0.178727   0.036242   4.931 9.16e-07 ***
PriceRangeLow:NeighborhoodSWISU       0.089124   0.042612   2.092 0.036667 *  
PriceRangeHigh:NeighborhoodTimber     0.339333   0.028881  11.750  < 2e-16 ***
PriceRangeMiddle:NeighborhoodTimber   0.256708   0.039830   6.445 1.60e-10 ***
PriceRangeLow:NeighborhoodTimber      0.000000   0.000000      NA       NA    
PriceRangeHigh:NeighborhoodVeenker    0.430892   0.050392   8.551  < 2e-16 ***
PriceRangeMiddle:NeighborhoodVeenker  0.223013   0.063329   3.521 0.000443 ***
PriceRangeLow:NeighborhoodVeenker     0.000000   0.000000      NA       NA    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Approximate significance of smooth terms:
                               edf Ref.df       F  p-value    
s(TotalSF):PriceRangeHigh    1.241  1.446 294.701  < 2e-16 ***
s(TotalSF):PriceRangeMiddle  2.140  2.761  93.705  < 2e-16 ***
s(TotalSF):PriceRangeLow     4.702  5.592  42.010  < 2e-16 ***
s(QualCond):PriceRangeHigh   2.674  3.371  19.324 2.40e-13 ***
s(QualCond):PriceRangeMiddle 4.388  5.219  12.204 7.99e-12 ***
s(QualCond):PriceRangeLow    2.622  3.300  51.032  < 2e-16 ***
s(Age)                       5.904  7.059  10.842 1.94e-13 ***
s(MSSubClass)                7.261  8.134   8.372 2.45e-11 ***
s(GarageArea):GarageCars     2.000  2.000  62.900  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Rank: 136/161
R-sq.(adj) =    0.9   Deviance explained = 90.6%
GCV = 0.016601  Scale est. = 0.01561   n = 1456

plot(ames.gam.base)

ord.lm = lm(logPrice ~ OrdMaster:PriceRange, data=ames.test)
plot(ord.lm)

influencePlot(ord.lm)

ames.test = ames %>% mutate(OrdMaster = QualCond*ExterCond*ExterQual)
ord.gam = mgcv::gam(logPrice ~ 
                      s(OrdMaster)
                    + s(TotalSF, by=PriceRange)
                    + Neighborhood,
                    data=ames.test)
plot(ord.gam)

summary(ord.gam)

Family: gaussian 
Link function: identity 

Formula:
logPrice ~ s(OrdMaster) + s(TotalSF, by = PriceRange) + Neighborhood

Parametric coefficients:
                     Estimate Std. Error  t value Pr(>|t|)    
(Intercept)         11.558492   0.007796 1482.549  < 2e-16 ***
NeighborhoodBlmngtn  0.488561   0.035110   13.915  < 2e-16 ***
NeighborhoodBlueste  0.366787   0.098519    3.723 0.000205 ***
NeighborhoodBrDale   0.333030   0.038944    8.551  < 2e-16 ***
NeighborhoodBrkSide  0.352229   0.020852   16.892  < 2e-16 ***
NeighborhoodClearCr  0.550246   0.027602   19.935  < 2e-16 ***
NeighborhoodCollgCr  0.535031   0.013575   39.414  < 2e-16 ***
NeighborhoodCrawfor  0.508258   0.020932   24.282  < 2e-16 ***
NeighborhoodEdwards  0.366981   0.016198   22.657  < 2e-16 ***
NeighborhoodGilbert  0.592288   0.017595   33.663  < 2e-16 ***
NeighborhoodIDOTRR   0.236668   0.027586    8.579  < 2e-16 ***
NeighborhoodMeadowV  0.324627   0.037962    8.551  < 2e-16 ***
NeighborhoodMitchel  0.455044   0.021403   21.261  < 2e-16 ***
NeighborhoodNAmes    0.403216   0.012168   33.139  < 2e-16 ***
NeighborhoodNoRidge  0.595490   0.025708   23.164  < 2e-16 ***
NeighborhoodNPkVill  0.412112   0.047299    8.713  < 2e-16 ***
NeighborhoodNridgHt  0.650982   0.019188   33.926  < 2e-16 ***
NeighborhoodNWAmes   0.445037   0.018211   24.437  < 2e-16 ***
NeighborhoodOldTown  0.259588   0.016026   16.198  < 2e-16 ***
NeighborhoodSawyer   0.398217   0.018231   21.843  < 2e-16 ***
NeighborhoodSawyerW  0.510025   0.019298   26.429  < 2e-16 ***
NeighborhoodSomerst  0.616710   0.017029   36.215  < 2e-16 ***
NeighborhoodStoneBr  0.668427   0.029585   22.594  < 2e-16 ***
NeighborhoodSWISU    0.318565   0.029128   10.937  < 2e-16 ***
NeighborhoodTimber   0.584961   0.023983   24.391  < 2e-16 ***
NeighborhoodVeenker  0.585412   0.043028   13.605  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Approximate significance of smooth terms:
                              edf Ref.df      F p-value    
s(OrdMaster)                6.716  7.725  56.53  <2e-16 ***
s(TotalSF):PriceRangeHigh   3.902  4.899 188.03  <2e-16 ***
s(TotalSF):PriceRangeMiddle 2.503  3.180 180.99  <2e-16 ***
s(TotalSF):PriceRangeLow    8.788  8.978  38.75  <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Rank: 61/62
R-sq.(adj) =  0.867   Deviance explained = 87.1%
GCV = 0.021533  Scale est. = 0.02084   n = 1456
#plot(ames.lm)
#library(car)
inf = influencePlot(ames.lm)

ames[rownames(inf),] %>% select(c(Age,Neighborhood,QualCond,PriceRange,TotalSF))
rownames(inf)
[1] "218" "564" "575" "804" "900" "963"

ames.filt = ames[as.numeric(rownames(inf)),]
ames %>% anti_join(., ames.filt, by = c("SecFlrSF","Neighborhood","FirstFlrSF"))
## Base GAM Model
ames.gam.base <- mgcv::gam(logPrice
                ~ s(TotalSF, by=PriceRange)
                #+ s(TotalBsmtSF)
                + s(QualCond, by=PriceRange)
                + s(Age)
                + Fireplaces:PriceRange
                + s(MSSubClass)
                #+ PriceRange
                + Neighborhood:PriceRange
                + s(GarageArea, by=GarageCars),
                #+ FullBath,
                method='GCV.Cp',
                data=ames)

## Base LM Model
ames.lm <- lm(logPrice
              ~ TotalSF:PriceRange
              #+ TotalBsmtSF
              + QualCond:PriceRange
              + Age
              + Fireplaces:PriceRange
              + MSSubClass
              #+ PriceRange
              + Neighborhood:PriceRange
              + GarageArea:GarageCars,
              #+ FullBath,
              #method='GCV.CP',
              data=ames)

true = ames_test[,'SalePrice'][[1]]

# gam.predictions = predict.gam(ames.gam, newdata = ames_test, type = 'response')
# gam.errors = gam.predictions - log(true)
# gam.diff =exp(gam.predictions) - true

gam.base.predictions = predict.gam(ames.gam.base, newdata = ames_test, type = 'response')
gam.base.errors = gam.base.predictions - log(true)
gam.base.diff =exp(gam.base.predictions) - true

lm.predictions = predict(ames.lm, newdata = ames_test)
lm.errors = lm.predictions - log(true)
lm.diff = exp(lm.predictions) - true

print(paste('GAM RMSE:',sqrt(mean(gam.base.errors^2))))
print(paste('LM RMSE:',sqrt(mean(lm.errors^2))))

print(paste('GAM: Error in $ as Predicted:',round(mean(abs(gam.base.diff)),2)))
print(paste('LM: Error in $ as Predicted:',round(mean(abs(lm.diff)),2)))

summary(ames.gam.base)

ames$PriceRange <- factor(ames$PriceRange, levels = c("Low", "Middle", "High"))

ames.gam.test = mgcv::gam(logPrice ~
                            s(OverallQual, by=PriceRange, bs='gp')
                          + s(TotalSF, by=PriceRange, bs='gp'),
                          method='ML',
                          data=ames)


test.preds= test %>% mutate(logPrice = predict.gam(ames.gam.test, newdata = test, type = 'response'))

gam_qual_eda = visreg(ames.gam.test, 'OverallQual', partial=TRUE,
       #scale='response',
       alpha=0.05, gg=TRUE, 
       line=list(col="red"),
       fill=list(fill="pink"),
       points=list(size=1, pch=1, alpha=0.4, col='black')) + 
  theme_bw() +
  labs(y = 'Log Sale Price', x = 'Overall Quality') +
  geom_smooth(method='lm', color='blue') +
  scale_color_manual(values=colors)

ggsave(paste0("./presentation/gam_qual_eda.png"), gam_qual_eda)
Saving 7 x 7 in image
gam_qual_edaP = visreg(ames.gam.test, 'OverallQual', by='PriceRange', partial=TRUE,
       #scale='response',
       alpha=0.05, gg=TRUE, 
       line=list(col="red"),
       fill=list(fill="pink"),
       points=list(size=1, pch=1, alpha=0.4, col='black')) + 
  ylim(10,15) +
  theme_bw() +
  labs(y = 'Log Sale Price', x = 'Overall Quality') +
  scale_color_manual(values=colors)

ggsave(paste0("./presentation/gam_qual_edaP.png"), gam_qual_edaP)

visreg(ames.gam.test, 'TotalSF', partial=TRUE,
       #scale='response',
       alpha=0.05, gg=TRUE, 
       line=list(col="red"),
       fill=list(fill="pink"),
       points=list(size=1, pch=1, alpha=0.4, col='black')) + 
  theme_bw() +
  labs(y = 'Log Sale Price', x = 'Total Home SF') +
  geom_smooth(method='lm', color='blue') +
  scale_color_manual(values=colors)



visreg(ames.gam.test, 'TotalSF', by='PriceRange', partial=TRUE,
       #scale='response',
       alpha=0.05, gg=TRUE, 
       line=list(col="red"),
       fill=list(fill="pink"),
       points=list(size=1, pch=1, alpha=0.4, col='black')) + 
  ylim(10,15) +
  theme_bw() +
  labs(y = 'Log Sale Price', x = 'Total Home SF') +
  scale_color_manual(values=colors)

#visreg(ames.gam, 'MSSubClass',by='PriceRange', overlay=TRUE)

# fit <- lm(log(SalePrice) ~ poly(GrLivArea, 2)*poly(OverallQual, 2), data=ames)
# visreg2d(fit, "GrLivArea", "OverallQual")
# 
# visreg(ames.gam, 'TotalBsmtSF',by='HasGarage', overlay=TRUE)

grliv = visreg(ames.gam.base, 'TotalSF', partial=TRUE,
       #scale='response',
       alpha=0.05, gg=TRUE, 
       line=list(col="red"),
       fill=list(fill="pink"),
       points=list(size=1, pch=1, alpha=0.4, col='black')) + 
  theme_bw() +
  labs(y = 'Log Sale Price', x = 'Total Home SF') +
  geom_smooth(method='lm', color='blue') +
  scale_color_manual(values=colors)

ggsave(paste0("./presentation/gam_grliv.png"), grliv)

grliv_price = visreg(ames.gam.base, 'TotalSF', by='PriceRange', partial=TRUE,
       #scale='response',
       alpha=0.05, gg=TRUE, 
       line=list(col="red"),
       fill=list(fill="pink"),
       points=list(size=1, pch=1, alpha=0.4, col='black')) + 
  theme_bw() +
  labs(y = 'Log Sale Price', x = 'Total Home SF') +
  scale_color_manual(values=colors)


ggsave(paste0("./presentation/gam_grliv_price.png"), grliv_price)

# bsmt = visreg(ames.gam.base, 'TotalBsmtSF', partial=TRUE,
#        #scale='response',
#        alpha=0.05, gg=TRUE, 
#        line=list(col="red"),
#        fill=list(fill="pink"),
#        points=list(size=1, pch=1, alpha=0.2, col='black')) + 
#   theme_bw() +
#   labs(y = 'Log Sale Price', x = 'Total Basement SF') +
#   geom_smooth(method='lm', color='blue') +
#   scale_color_manual(values=colors)

ggsave(paste0("./presentation/gam_bsmt.png"), bsmt)

msub = visreg(ames.gam.base, 'MSSubClass', partial=TRUE,
              jitter=TRUE,
              alpha=0.05, gg=TRUE, 
              line=list(col="red"),
              fill=list(fill="pink"),
              points=list(size=1, pch=1, alpha=0.2, col='black')) + 
  theme_bw() +
  labs(y = 'Log Sale Price', x = 'MSSubClass: Type of Dwelling') +
  geom_smooth(method='lm', color='blue') +
  scale_color_manual(values=colors)

ggsave(paste0("./presentation/gam_msub.png"), msub)

# cond = visreg(ames.gam.base, 'OverallCond', partial=TRUE,
#        #scale='response',
#        alpha=0.05, gg=TRUE, 
#               jitter=TRUE,
#        line=list(col="red"),
#        fill=list(fill="pink"),
#        points=list(size=1, pch=1, alpha=0.2, col='black')) + 
#   theme_bw() +
#   labs(y = 'Log Sale Price', x = 'Overall Condition of Home') +
#   geom_smooth(method='lm', color='blue') +
#   scale_color_manual(values=colors)
# 
# 
# ggsave(paste0("./presentation/gam_cond.png"), cond)

garage = visreg(ames.gam.base, 'GarageArea', by='GarageCars', partial=TRUE,
       #scale='response',
       alpha=0.05, gg=TRUE, 
       line=list(col="red"),
              jitter=TRUE,
       fill=list(fill="pink"),
       points=list(size=1, pch=1, alpha=0.2, col='black')) + 
  geom_smooth(method='lm', color='blue') +
  theme_bw() +
  labs(y = 'Log Sale Price', x = 'Garage Area')

ggsave(paste0("./presentation/gam_garage.png"), garage)

age = visreg(ames.gam.base, 'Age', partial=TRUE,
       #scale='response',
       alpha=0.05, gg=TRUE, 
       line=list(col="red"),
       fill=list(fill="pink"),
       points=list(size=1, pch=1, alpha=0.2, col='black')) + 
  theme_bw() +
  labs(y = 'Log Sale Price', x = 'Age of Home') +
  geom_smooth(method='lm', color='blue') +
  scale_color_manual(values=colors)

  # geom_smooth(method='lm', color='blue') +
  # scale_color_manual(values=colors)
ggsave(paste0("./presentation/gam_age.png"), age)
## Base GAM Model

ames.gam.test <- mgcv::gam(logPrice
                ~ s(QualCond, by=PriceRange)
                + s(TotalSF, by=PriceRange),
                method='GCV.Cp',
                data=ames)

## Base LM Model
ames.lm.test <- lm(logPrice
              ~ QualCond:PriceRange
              + TotalSF:PriceRange,
              data=ames)

true = ames_test[,'SalePrice'][[1]]

summary(ames.gam.test)
#summary(ames.lm.test)
visreg(ames.gam.base, "QualCond", partial=TRUE,
       #scale='response',
       
       breaks = c(1000,2000,3000),
       alpha=0.05, gg=TRUE) 

visreg(ames.gam.base, "QualCond", by="PriceRange",
       scale='response',
       
       alpha=0.05, gg=TRUE)  + ylim(10,15)

visreg(ames.gam.base, "PriceRange", by='TotalSF', partial=TRUE,
       scale='response',
       
       overlay=TRUE, breaks = c(1000,2000,3000),
       alpha=0.05, gg=TRUE) 


visreg(ames.lm, "PriceRange", by='TotalSF', partial=TRUE,
       scale='response',
       breaks = c(1500,2500,3500),
       alpha=0.01, gg=TRUE) 


visreg(ames.lm, "TotalSF", by='PriceRange', partial=TRUE,
       scale='response',
       #breaks = c(1500,2500,3500),
       alpha=0.01, gg=TRUE) 

visreg(ames.gam.base, 'TotalSF',partial=TRUE,
       #scale='response',
       alpha=0.05, gg=TRUE, 
       line=list(col="red"),
       fill=list(fill="pink"),
       points=list(size=1, pch=1, alpha=0.4, col='black')) + 
  theme_bw() +
  geom_smooth(method = 'lm')
  labs(y = 'Log Sale Price', x = 'Total Home SF') +
  scale_color_manual(values=colors)

visreg(ames.gam.base, 'TotalSF', by='PriceRange', partial=TRUE,
       #scale='response',
       alpha=0.05, gg=TRUE, 
       line=list(col="red"),
       fill=list(fill="pink"),
       points=list(size=1, pch=1, alpha=0.4, col='black')) + 
  theme_bw() +
  labs(y = 'Log Sale Price', x = 'Total Home SF') +
  ylim(10,15)+
  scale_color_manual(values=colors)
visreg(ames.lm, "PriceRange", by="TotalSF", partial=TRUE,
       #scale='response',
       overlay=TRUE, breaks = c(1000,2000,3000),
       alpha=0.05, gg=TRUE) 

visreg(ames.gam.base, "PriceRange", by="TotalSF",, partial=TRUE,
       #scale='response',
       overlay=TRUE, breaks = c(1000,2000,3000),
       alpha=0.05, gg=TRUE)

std = sd(gam.base.errors)
datgam <- data.frame(density = c(gam.base.errors), model = rep(c("GAM")))
datlm <- data.frame(density = c(lm.errors), model = rep(c("LM")))
# dens_comp = ggplot(dat,aes(x = density, fill= model)) + 
#   # stat_function(fun = dnorm, args=list(0,std), aes(col='Gaussian')) +
#   geom_density(alpha=0.5) +
#   theme_bw() +
#   #scale_fill_brewer(palette="Dark2") +
#   labs(x='Residual Magnitude', y='Frequency')

datgam %>% 
  ggplot(aes(x=density)) +
  geom_density()


ggsave(paste0("./presentation/dens_comp.png"), dens_comp)
Saving 7.29 x 4.51 in image

plot(true, exp(gam.base.predictions), ylim=c(0,7e5), xlim=c(0,7e5))
abline(a=0,b=1)


#points(ames_test_[,'SalePrice'][[1]], exp(lm.predictions))
plot(true, exp(lm.predictions), ylim=c(0,7e5), xlim=c(0,7e5))
abline(a=0,b=1)

# gam.dollars = data.frame(cbind(exp(gam.base.predictions)-true, true))
# colnames(gam.dollars) = c('Error','Real')
# 
# ggplot(gam.dollars, aes(x=Real, y=Error)) + 
#   geom_point() +
#   theme_bw() +
#   ylim(-1e5,1e5) + 
#   xlim(0,7e5) +
#   labs(x = 'True Price [$]',y = 'Prediction Error [$]')
# 
# slope = mean(abs(gam.dollars$Error))/mean(gam.dollars$Real)
# #slope = mean(abs(gam.dollars$Error)/gam.dollars$Real)
# ggplot(gam.dollars, aes(x=Real, y=abs(Error))) + 
#   geom_point() +
#   geom_abline(intercept = 0, slope = 3*slope, linetype='dashed') +
#   geom_abline(intercept = 0, slope = 2*slope, linetype='dashed') +
#   geom_abline(intercept = 0, slope = 1*slope, linetype='dashed') +
#   geom_abline(intercept = 0, slope = 0*slope, linetype='dashed') +
#   theme_bw() +
#   #ylim(-1e5,1e5) + 
#   xlim(0,7e5) +
#   labs(x = 'True Price [$]',y = 'GAM Absolute Prediction Error [$]')
# 
# #ggsave(paste0("./presentation/res_gam.png"), res_gam)



gam.test_predictions = predict.gam(ames.gam.base, newdata = ames_test, type = 'response')
lm.test_predictions = predict(ames.lm, newdata = ames_test, type = 'response')
prediction from a rank-deficient fit may be misleading
gam.dollars = data.frame(cbind(gam.test_predictions, log(true), gam.test_predictions-log(true)))
colnames(gam.dollars) = c('Fitted','Real','Error')
gam.dollars$stdError = gam.dollars$Error/sd(gam.dollars$Error)

lm.dollars = data.frame(cbind(lm.test_predictions, log(true), lm.test_predictions-log(true)))
colnames(lm.dollars) = c('Fitted','Real','Error')
lm.dollars$stdError = lm.dollars$Error/sd(lm.dollars$Error)
 
#slope = mean(abs(gam.dollars$Error))/mean(gam.dollars$Real)

#gam_res = 
  ggplot(gam.dollars, aes(x=Fitted, y=stdError)) + 
  geom_point(alpha=0.2) +
  geom_smooth() +
  theme_bw() +
  #ylim(-1e5,1e5) + 
  #xlim(1,log(7e5)) +
  labs(x = 'GAM Fitted Values',y = 'Residual Error')


#ggsave(paste0("./presentation/gam_res.png"), gam_res)

 
#slope = mean(abs(gam.dollars$Error))/mean(gam.dollars$Real)

#lm_res = 
  ggplot(lm.dollars, aes(x=Fitted, y=stdError)) + 
  geom_point(alpha=0.2) +
  geom_smooth() +
  theme_bw() +
  #ylim(-1e5,1e5) + 
  #xlim(1,log(7e5)) +
  labs(x = 'LM Fitted Values',y = 'Residual Error')


#ggsave(paste0("./presentation/lm_res.png"), lm_res)
plot(ames.lm)
not plotting observations with leverage one:
  446, 788, 914, 1023

not plotting observations with leverage one:
  446, 788, 914, 1023

Testing Model Assumptions

gam.anal= data.frame(cbind(ames.gam.base$fitted.values, ames.gam.base$residuals))
colnames(gam.anal) = c('Fitted','Error')
gam.anal$stdError = ames.gam.base$residuals/sd(ames.gam.base$residuals)

gam.anal %>% 
  ggplot(aes(x=Fitted, y=stdError)) + 
  geom_point(alpha=0.2) +
  geom_smooth() +
  theme_bw() +
  #ylim(-1e5,1e5) + 
  #xlim(1,log(7e5)) +
  labs(x = 'GAM Fitted Values',y = 'Residual Error')



gam.anal %>% 
  ggplot(aes(x=Fitted, y=stdError^2)) + 
  geom_point(alpha=0.2) +
  geom_smooth() +
  theme_bw() +
  #ylim(-1e5,1e5) + 
  #xlim(1,log(7e5)) +
  labs(x = 'GAM Fitted Values',y = 'Residual Error')


gam.anal %>% 
  ggplot(aes(x=stdError)) +
  geom_density()

  theme_bw()
List of 93
 $ line                      :List of 6
  ..$ colour       : chr "black"
  ..$ size         : num 0.5
  ..$ linetype     : num 1
  ..$ lineend      : chr "butt"
  ..$ arrow        : logi FALSE
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_line" "element"
 $ rect                      :List of 5
  ..$ fill         : chr "white"
  ..$ colour       : chr "black"
  ..$ size         : num 0.5
  ..$ linetype     : num 1
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_rect" "element"
 $ text                      :List of 11
  ..$ family       : chr ""
  ..$ face         : chr "plain"
  ..$ colour       : chr "black"
  ..$ size         : num 11
  ..$ hjust        : num 0.5
  ..$ vjust        : num 0.5
  ..$ angle        : num 0
  ..$ lineheight   : num 0.9
  ..$ margin       : 'margin' num [1:4] 0pt 0pt 0pt 0pt
  .. ..- attr(*, "valid.unit")= int 8
  .. ..- attr(*, "unit")= chr "pt"
  ..$ debug        : logi FALSE
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ title                     : NULL
 $ aspect.ratio              : NULL
 $ axis.title                : NULL
 $ axis.title.x              :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : NULL
  ..$ vjust        : num 1
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 2.75pt 0pt 0pt 0pt
  .. ..- attr(*, "valid.unit")= int 8
  .. ..- attr(*, "unit")= chr "pt"
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.title.x.top          :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : NULL
  ..$ vjust        : num 0
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 0pt 0pt 2.75pt 0pt
  .. ..- attr(*, "valid.unit")= int 8
  .. ..- attr(*, "unit")= chr "pt"
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.title.x.bottom       : NULL
 $ axis.title.y              :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : NULL
  ..$ vjust        : num 1
  ..$ angle        : num 90
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 0pt 2.75pt 0pt 0pt
  .. ..- attr(*, "valid.unit")= int 8
  .. ..- attr(*, "unit")= chr "pt"
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.title.y.left         : NULL
 $ axis.title.y.right        :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : NULL
  ..$ vjust        : num 0
  ..$ angle        : num -90
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 0pt 0pt 0pt 2.75pt
  .. ..- attr(*, "valid.unit")= int 8
  .. ..- attr(*, "unit")= chr "pt"
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.text                 :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : chr "grey30"
  ..$ size         : 'rel' num 0.8
  ..$ hjust        : NULL
  ..$ vjust        : NULL
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.text.x               :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : NULL
  ..$ vjust        : num 1
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 2.2pt 0pt 0pt 0pt
  .. ..- attr(*, "valid.unit")= int 8
  .. ..- attr(*, "unit")= chr "pt"
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.text.x.top           :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : NULL
  ..$ vjust        : num 0
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 0pt 0pt 2.2pt 0pt
  .. ..- attr(*, "valid.unit")= int 8
  .. ..- attr(*, "unit")= chr "pt"
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.text.x.bottom        : NULL
 $ axis.text.y               :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : num 1
  ..$ vjust        : NULL
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 0pt 2.2pt 0pt 0pt
  .. ..- attr(*, "valid.unit")= int 8
  .. ..- attr(*, "unit")= chr "pt"
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.text.y.left          : NULL
 $ axis.text.y.right         :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : num 0
  ..$ vjust        : NULL
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 0pt 0pt 0pt 2.2pt
  .. ..- attr(*, "valid.unit")= int 8
  .. ..- attr(*, "unit")= chr "pt"
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.ticks                :List of 6
  ..$ colour       : chr "grey20"
  ..$ size         : NULL
  ..$ linetype     : NULL
  ..$ lineend      : NULL
  ..$ arrow        : logi FALSE
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_line" "element"
 $ axis.ticks.x              : NULL
 $ axis.ticks.x.top          : NULL
 $ axis.ticks.x.bottom       : NULL
 $ axis.ticks.y              : NULL
 $ axis.ticks.y.left         : NULL
 $ axis.ticks.y.right        : NULL
 $ axis.ticks.length         : 'unit' num 2.75pt
  ..- attr(*, "valid.unit")= int 8
  ..- attr(*, "unit")= chr "pt"
 $ axis.ticks.length.x       : NULL
 $ axis.ticks.length.x.top   : NULL
 $ axis.ticks.length.x.bottom: NULL
 $ axis.ticks.length.y       : NULL
 $ axis.ticks.length.y.left  : NULL
 $ axis.ticks.length.y.right : NULL
 $ axis.line                 : list()
  ..- attr(*, "class")= chr [1:2] "element_blank" "element"
 $ axis.line.x               : NULL
 $ axis.line.x.top           : NULL
 $ axis.line.x.bottom        : NULL
 $ axis.line.y               : NULL
 $ axis.line.y.left          : NULL
 $ axis.line.y.right         : NULL
 $ legend.background         :List of 5
  ..$ fill         : NULL
  ..$ colour       : logi NA
  ..$ size         : NULL
  ..$ linetype     : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_rect" "element"
 $ legend.margin             : 'margin' num [1:4] 5.5pt 5.5pt 5.5pt 5.5pt
  ..- attr(*, "valid.unit")= int 8
  ..- attr(*, "unit")= chr "pt"
 $ legend.spacing            : 'unit' num 11pt
  ..- attr(*, "valid.unit")= int 8
  ..- attr(*, "unit")= chr "pt"
 $ legend.spacing.x          : NULL
 $ legend.spacing.y          : NULL
 $ legend.key                :List of 5
  ..$ fill         : chr "white"
  ..$ colour       : logi NA
  ..$ size         : NULL
  ..$ linetype     : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_rect" "element"
 $ legend.key.size           : 'unit' num 1.2lines
  ..- attr(*, "valid.unit")= int 3
  ..- attr(*, "unit")= chr "lines"
 $ legend.key.height         : NULL
 $ legend.key.width          : NULL
 $ legend.text               :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : 'rel' num 0.8
  ..$ hjust        : NULL
  ..$ vjust        : NULL
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ legend.text.align         : NULL
 $ legend.title              :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : num 0
  ..$ vjust        : NULL
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ legend.title.align        : NULL
 $ legend.position           : chr "right"
 $ legend.direction          : NULL
 $ legend.justification      : chr "center"
 $ legend.box                : NULL
 $ legend.box.just           : NULL
 $ legend.box.margin         : 'margin' num [1:4] 0cm 0cm 0cm 0cm
  ..- attr(*, "valid.unit")= int 1
  ..- attr(*, "unit")= chr "cm"
 $ legend.box.background     : list()
  ..- attr(*, "class")= chr [1:2] "element_blank" "element"
 $ legend.box.spacing        : 'unit' num 11pt
  ..- attr(*, "valid.unit")= int 8
  ..- attr(*, "unit")= chr "pt"
 $ panel.background          :List of 5
  ..$ fill         : chr "white"
  ..$ colour       : logi NA
  ..$ size         : NULL
  ..$ linetype     : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_rect" "element"
 $ panel.border              :List of 5
  ..$ fill         : logi NA
  ..$ colour       : chr "grey20"
  ..$ size         : NULL
  ..$ linetype     : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_rect" "element"
 $ panel.spacing             : 'unit' num 5.5pt
  ..- attr(*, "valid.unit")= int 8
  ..- attr(*, "unit")= chr "pt"
 $ panel.spacing.x           : NULL
 $ panel.spacing.y           : NULL
 $ panel.grid                :List of 6
  ..$ colour       : chr "grey92"
  ..$ size         : NULL
  ..$ linetype     : NULL
  ..$ lineend      : NULL
  ..$ arrow        : logi FALSE
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_line" "element"
 $ panel.grid.major          : NULL
 $ panel.grid.minor          :List of 6
  ..$ colour       : NULL
  ..$ size         : 'rel' num 0.5
  ..$ linetype     : NULL
  ..$ lineend      : NULL
  ..$ arrow        : logi FALSE
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_line" "element"
 $ panel.grid.major.x        : NULL
 $ panel.grid.major.y        : NULL
 $ panel.grid.minor.x        : NULL
 $ panel.grid.minor.y        : NULL
 $ panel.ontop               : logi FALSE
 $ plot.background           :List of 5
  ..$ fill         : NULL
  ..$ colour       : chr "white"
  ..$ size         : NULL
  ..$ linetype     : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_rect" "element"
 $ plot.title                :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : 'rel' num 1.2
  ..$ hjust        : num 0
  ..$ vjust        : num 1
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 0pt 0pt 5.5pt 0pt
  .. ..- attr(*, "valid.unit")= int 8
  .. ..- attr(*, "unit")= chr "pt"
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ plot.title.position       : chr "panel"
 $ plot.subtitle             :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : num 0
  ..$ vjust        : num 1
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 0pt 0pt 5.5pt 0pt
  .. ..- attr(*, "valid.unit")= int 8
  .. ..- attr(*, "unit")= chr "pt"
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ plot.caption              :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : 'rel' num 0.8
  ..$ hjust        : num 1
  ..$ vjust        : num 1
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 5.5pt 0pt 0pt 0pt
  .. ..- attr(*, "valid.unit")= int 8
  .. ..- attr(*, "unit")= chr "pt"
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ plot.caption.position     : chr "panel"
 $ plot.tag                  :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : 'rel' num 1.2
  ..$ hjust        : num 0.5
  ..$ vjust        : num 0.5
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ plot.tag.position         : chr "topleft"
 $ plot.margin               : 'margin' num [1:4] 5.5pt 5.5pt 5.5pt 5.5pt
  ..- attr(*, "valid.unit")= int 8
  ..- attr(*, "unit")= chr "pt"
 $ strip.background          :List of 5
  ..$ fill         : chr "grey85"
  ..$ colour       : chr "grey20"
  ..$ size         : NULL
  ..$ linetype     : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_rect" "element"
 $ strip.background.x        : NULL
 $ strip.background.y        : NULL
 $ strip.placement           : chr "inside"
 $ strip.text                :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : chr "grey10"
  ..$ size         : 'rel' num 0.8
  ..$ hjust        : NULL
  ..$ vjust        : NULL
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 4.4pt 4.4pt 4.4pt 4.4pt
  .. ..- attr(*, "valid.unit")= int 8
  .. ..- attr(*, "unit")= chr "pt"
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ strip.text.x              : NULL
 $ strip.text.y              :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : NULL
  ..$ vjust        : NULL
  ..$ angle        : num -90
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ strip.switch.pad.grid     : 'unit' num 2.75pt
  ..- attr(*, "valid.unit")= int 8
  ..- attr(*, "unit")= chr "pt"
 $ strip.switch.pad.wrap     : 'unit' num 2.75pt
  ..- attr(*, "valid.unit")= int 8
  ..- attr(*, "unit")= chr "pt"
 $ strip.text.y.left         :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : NULL
  ..$ vjust        : NULL
  ..$ angle        : num 90
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 - attr(*, "class")= chr [1:2] "theme" "gg"
 - attr(*, "complete")= logi TRUE
 - attr(*, "validate")= logi TRUE
  
gam.anal %>% 
  ggplot(aes(x = sample(1:nrow(gam.anal)), y = stdError)) +
  geom_point(alpha=0.5) +
  geom_hline(yintercept=qt(0.025, df = nrow(gam.anal) - 2), color = "red", linetype="dashed", color = "red") +
  geom_hline(yintercept=qt(1 - 0.025, df = nrow(gam.anal) - 2), color = "red", linetype="dashed", color = "red") +
  labs(x = 'Train Set Index', y = 'Standardized Residuals') +
  theme_bw()
Duplicated aesthetics after name standardisation: colourDuplicated aesthetics after name standardisation: colour

  
  
  
lm.anal= data.frame(cbind(ames.lm$fitted.values, ames.lm$residuals))
colnames(lm.anal) = c('Fitted','Error')
lm.anal$stdError = ames.lm$residuals/sd(ames.lm$residuals)

lm.anal %>% 
  ggplot(aes(x=Fitted, y=stdError)) + 
  geom_point(alpha=0.2) +
  geom_smooth() +
  theme_bw() +
  labs(x = 'LM Fitted Values',y = 'Residual Error')


lm.anal %>% 
  ggplot(aes(x = sample(1:nrow(lm.anal)), y = stdError)) +
  geom_point(alpha=0.5) +
  geom_hline(yintercept=qt(0.025, df = nrow(lm.anal) - 2), color = "red", linetype="dashed", color = "red") +
  geom_hline(yintercept=qt(1 - 0.025, df = nrow(lm.anal) - 2), color = "red", linetype="dashed", color = "red") +
  labs(x = 'Train Set Index', y = 'Standardized Residuals') +
  theme_bw()
Duplicated aesthetics after name standardisation: colourDuplicated aesthetics after name standardisation: colour

lm.dollars = data.frame(cbind(exp(lm.predictions)-true, true))
colnames(lm.dollars) = c('Error','Real')

ggplot(lm.dollars, aes(x=Real, y=Error)) + 
  geom_point() +
  theme_bw() +
  ylim(-1e5,1e5) + 
  xlim(0,7e5) +
  labs(x = 'True Price [$]',y = 'Prediction Error [$]')

slope = mean(abs(lm.dollars$Error))/mean(lm.dollars$Real)
#slope = mean(abs(gam.dollars$Error)/gam.dollars$Real)
res_lm = ggplot(lm.dollars, aes(x=Real, y=abs(Error))) + 
  geom_point() +
  geom_abline(intercept = 0, slope = 3*slope, linetype='dashed') +
  geom_abline(intercept = 0, slope = 2*slope, linetype='dashed') +
  geom_abline(intercept = 0, slope = 1*slope, linetype='dashed') +
  geom_abline(intercept = 0, slope = 0*slope, linetype='dashed') +
  theme_bw() +
  #ylim(-1e5,1e5) + 
  xlim(0,7e5) +
  labs(x = 'True Price [$]',y = 'LM Absolute Prediction Error [$]')


ggsave(paste0("./presentation/res_lm.png"), res_lm)

lm.dollars = data.frame(cbind(lm.predictions-log(true), log(true)))
colnames(lm.dollars) = c('Error','Real')

ggplot(lm.dollars, aes(x=Real, y=Error)) + 
  geom_point() +
  theme_bw() +
  #ylim(-1e5,1e5) + 
  #xlim(0,7e5) +
  labs(x = 'Log True Price',y = 'LM Prediction Error')

slope = mean(abs(lm.dollars$Error))/mean(lm.dollars$Real)
#slope = mean(abs(gam.dollars$Error)/gam.dollars$Real)
log_res_lm = ggplot(lm.dollars, aes(x=Real, y=abs(Error))) + 
  geom_point() +
  geom_abline(intercept = 0, slope = 3*slope, linetype='dashed') +
  geom_abline(intercept = 0, slope = 2*slope, linetype='dashed') +
  geom_abline(intercept = 0, slope = 1*slope, linetype='dashed') +
  geom_abline(intercept = 0, slope = 0*slope, linetype='dashed') +
  theme_bw() +
  #ylim(-1e5,1e5) + 
  #xlim(1,log(7e5)) +
  labs(x = 'Log True Price',y = 'LM Absolute Prediction Error')


ggsave(paste0("./presentation/log_res_lm.png"), log_res_lm)
ames.gam <- mgcv::gam(log(SalePrice)
                ~ s(GrLivArea, by=PriceRange, bs='cs', id=1)
                + s(YearBuilt)
                + ti(OverallQual,OverallCond)
                + s(MSSubClass)
                + s(YearBuilt)
                + CentralAir
                ,method='GCV.Cp', data=ames_train, gamma=1.4, select=TRUE)

visreg(ames.gam)

true = ames_test[,'SalePrice'][[1]]

gam.predictions = predict.gam(ames.gam, newdata = ames_test, type = 'response')
gam.errors = gam.predictions - log(true)
gam.diff =exp(gam.predictions) - true

plot(density(gam.errors), col ='red')
lines(density(gam.base.errors), col='black', lty=2)
#plot(ames.gam)
legend("topright",legend=c('GAM Residuals','Base GAM Residuals'),
       col=c("red","black"), lty=1:2, cex=0.8)


print(paste('GAM RMSE:',sqrt(mean(gam.errors^2))))

print(paste('GAM: Error in $ as Predicted:',round(mean(abs(gam.diff)),2)))

#plot(ames_test[,'SalePrice'][[1]], gam.errors)

plot(true, exp(gam.predictions), ylim=c(0,7e5), xlim=c(0,7e5))
abline(a=0,b=1)

summary(ames.gam)


gam.test_predictions = predict.gam(ames.gam.base, newdata = test, type = 'response')
submission = data.frame(exp(gam.test_predictions))
colnames(submission) = 'SalePrice'
submission = tibble::rowid_to_column(submission,'Id')
rownames(submission) = 1461:(nrow(submission)+1460)
gmb_submission = mutate(submission, Id = Id + 1460)

lm.test_predictions = predict(ames.lm, newdata = test, type = 'response')
lm_submission = data.frame(exp(lm.test_predictions))
colnames(lm_submission) = 'SalePrice'
lm_submission = tibble::rowid_to_column(lm_submission,'Id')
rownames(lm_submission) = 1461:(nrow(lm_submission)+1460)
lm_submission = mutate(lm_submission, Id = Id + 1460)
#write.csv(gam.test_predictions,'./data/price_predictions.csv')
# write.table(gm_submission,file="./data/gm_predictions.csv",col.names = c("Id","SalePrice"),sep = ",",row.names = F)
write.table(lm_submission,file="./data/lm_predictions.csv",col.names = c("Id","SalePrice"),sep = ",",row.names = F)
write.table(gmb_submission,file="./data/gmb_predictions.csv",col.names = c("Id","SalePrice"),sep = ",",row.names = F)

write.table((lm_submission+gmb_submission)/2,file="./data/avg_predictions.csv",col.names = c("Id","SalePrice"),sep = ",",row.names = F)
dim_pred = read_csv('../Dmitri/predictions_1590435442.csv')
Parsed with column specification:
cols(
  Id = col_double(),
  SalePrice = col_double()
)
test.preds = test %>% mutate(preds = predict.gam(ames.gam.base, newdata = test, type = 'response'), logpreds = log(preds))

test.preds.lm = test %>% mutate(preds = predict(ames.lm, newdata = test, type = 'response'), logpreds = log(preds))
prediction from a rank-deficient fit may be misleading
test.preds.dim = test %>% mutate(preds = dim_pred$SalePrice, logpreds = log(preds))

test.preds$PriceRange <- factor(test.preds$PriceRange, levels = c("High", "Middle", "Low"))
preds_unstruct = 
  test.preds %>% 
  ggplot(aes(x=1:nrow(test.preds), y = preds, col=PriceRange)) + 
  geom_point() +
  labs(x = 'Test Set Index', y = 'Price Predictions') +
  theme_bw()

ggsave(paste0("./presentation/preds_gam/unstruct.png"), preds_unstruct)
Saving 7 x 7 in image
preds_struct = 
  test.preds %>% 
  arrange(preds) %>% 
  ggplot(aes(x=1:nrow(test.preds), y = preds, col=PriceRange)) + 
  geom_point() +
  labs(x = 'Test Set Index', y = 'Price Predictions') +
  theme_bw()


ggsave(paste0("./presentation/preds_gam/struct.png"), preds_struct)


preds_struct_log = 
  test.preds %>% 
  arrange(logpreds) %>% 
  ggplot(aes(x=1:nrow(test.preds), y = logpreds, col=PriceRange)) + 
  geom_point() +
  labs(x = 'Test Set Index', y = 'Log Price Predictions') +
  theme_bw()


ggsave(paste0("./presentation/preds_gam/struct_log.png"), preds_struct_log)


preds_binned = 
  test.preds %>% 
  arrange(logpreds) %>% 
  ggplot(aes(x=1:nrow(test.preds), y = logpreds, col=PriceRange)) + 
  geom_point() +
  facet_wrap('PriceRange') +
  labs(x = 'Test Set Index', y = 'Log Price Predictions') +
  theme_bw()


ggsave(paste0("./presentation/preds_gam/binned.png"), preds_binned)

quants = 
  ggplot(test.preds, aes(sample = logpreds, colour = PriceRange)) +
  stat_qq() +
  stat_qq_line()


ggsave(paste0("./presentation/preds_gam/quants.png"), quants)
test.preds.lm$PriceRange <- factor(test.preds.lm$PriceRange, levels = c("High", "Middle", "Low"))
preds_unstruct = 
  test.preds.lm %>% 
  ggplot(aes(x=1:nrow(test.preds), y = preds, col=PriceRange)) + 
  geom_point() +
  labs(x = 'Test Set Index', y = 'Price Predictions') +
  theme_bw()

ggsave(paste0("./presentation/preds_lm/unstruct.png"), preds_unstruct)
Saving 7 x 7 in image
preds_struct = 
  test.preds.lm %>% 
  arrange(preds) %>% 
  ggplot(aes(x=1:nrow(test.preds), y = preds, col=PriceRange)) + 
  geom_point() +
  labs(x = 'Test Set Index', y = 'Price Predictions') +
  theme_bw()


ggsave(paste0("./presentation/preds_lm/truct.png"), preds_struct)


preds_struct_log = 
  test.preds.lm %>% 
  arrange(logpreds) %>% 
  ggplot(aes(x=1:nrow(test.preds), y = logpreds, col=PriceRange)) + 
  geom_point() +
  labs(x = 'Test Set Index', y = 'Log Price Predictions') +
  theme_bw()


ggsave(paste0("./presentation/preds_lm/struct_log.png"), preds_struct_log)


preds_binned = 
  test.preds.lm %>% 
  arrange(logpreds) %>% 
  ggplot(aes(x=1:nrow(test.preds), y = logpreds, col=PriceRange)) + 
  geom_point() +
  facet_wrap('PriceRange') +
  labs(x = 'Test Set Index', y = 'Log Price Predictions') +
  theme_bw()


ggsave(paste0("./presentation/preds_lm/binned.png"), preds_binned)

quants = 
  ggplot(test.preds.lm, aes(sample = logpreds, colour = PriceRange)) +
  stat_qq() +
  stat_qq_line()


ggsave(paste0("./presentation/preds_lm/quants.png"), quants)
test.preds.dim$PriceRange <- factor(test.preds.dim$PriceRange, levels = c("High", "Middle", "Low"))
preds_unstruct = 
  test.preds.dim %>% 
  ggplot(aes(x=1:nrow(test.preds), y = preds, col=PriceRange)) + 
  geom_point() +
  labs(x = 'Test Set Index', y = 'Price Predictions') +
  theme_bw()

ggsave(paste0("./presentation/preds_dim/unstruct.png"), preds_unstruct)
Saving 7 x 7 in image
preds_struct = 
  test.preds.dim %>% 
  arrange(preds) %>% 
  ggplot(aes(x=1:nrow(test.preds), y = preds, col=PriceRange)) + 
  geom_point() +
  labs(x = 'Test Set Index', y = 'Price Predictions') +
  theme_bw()


ggsave(paste0("./presentation/preds_dim/truct.png"), preds_struct)


preds_struct_log = 
  test.preds.dim %>% 
  arrange(log(preds)) %>% 
  ggplot(aes(x=1:nrow(test.preds), y = logpreds, col=PriceRange)) + 
  geom_point() +
  labs(x = 'Test Set Index', y = 'Log Price Predictions') +
  theme_bw()


ggsave(paste0("./presentation/preds_dim/struct_log.png"), preds_struct_log)


preds_binned = 
  test.preds.dim %>% 
  arrange(log(preds)) %>% 
  ggplot(aes(x=1:nrow(test.preds), y = logpreds, col=PriceRange)) + 
  geom_point() +
  facet_wrap('PriceRange') +
  labs(x = 'Test Set Index', y = 'Log Price Predictions') +
  theme_bw()


ggsave(paste0("./presentation/preds_dim/binned.png"), preds_binned)

quants = 
  ggplot(test.preds.dim, aes(sample = logpreds, colour = PriceRange)) +
  stat_qq() +
  labs(y = 'Log Price') +
  stat_qq_line()


ggsave(paste0("./presentation/preds_dim/quants.png"), quants)

stat_box_data <- function(y, upper_limit = max(ames$SalePrice) * 1.15) {
  return( 
    data.frame(
      y = 0.98 * upper_limit,
      label = paste('c\n', length(y), '\n')
    )
  )
}


g = ames %>% 
  ggplot(aes(x = reorder(Neighborhood,SalePrice, mean), y = SalePrice)) + 
  geom_boxplot() +
  stat_summary(
    fun.data = stat_box_data, 
    geom = "text", 
    hjust = 0.5,
    vjust = 0.9
  ) + 
  theme_bw() +
  theme(axis.text.x=element_text(angle=45, hjust=1)) +
  labs(x='Neighborhood', y = 'SalePrice')


ggsave(paste0("./Linear Model/hood_price.png"), g)

min_n = 20
# ames_train %>%
#   group_by(Neighborhood, OverallCond) %>%
#   tally() %>%
#   filter(n > min_n) %>%
#   ggplot(aes(x=Neighborhood, y=OverallCond)) +
#   geom_point() +
#   geom_text(color='red',size=4,aes(y=OverallCond+0.2, label=n)) +
#   theme_bw() +
#   theme(axis.text.x=element_text(angle=45, hjust=1))
#
# ames_train %>%
#   group_by(MSSubClass, OverallQual) %>%
#   tally() %>%
#   filter(n > min_n) %>%
#   ggplot(aes(x=factor(MSSubClass), y=OverallQual)) +
#   geom_point() +
#   geom_text(color='red',size=4,aes(y=OverallQual+0.2, label=n)) +
#   theme_bw() +
#   theme(axis.text.x=element_text(angle=45, hjust=1))
#
#
# ames_train %>%
#   group_by(HouseStyle, OverallQual) %>%
#   tally() %>%
#   filter(n > min_n) %>%
#   ggplot(aes(x=factor(HouseStyle), y=OverallQual)) +
#   geom_point() +
#   geom_text(color='red',size=4,aes(y=OverallQual+0.2, label=n)) +
#   theme_bw() +
#   theme(axis.text.x=element_text(angle=45, hjust=1))

# Zoning relationship wrt Overall Quality of the Home
# Floating Village Residential has score >= 6
# Commercial zones have scores <= 6
# Low density residential spans all scores
# medium density tends towards higher scores

# ames_train %>%
#   group_by(MSZoning, OverallQual) %>%
#   tally() %>%
#   filter(n > min_n) %>%
#   ggplot(aes(x=factor(MSZoning), y=OverallQual)) +
#   geom_point() +
#   geom_text(color='red',size=4,aes(y=OverallQual+0.2, label=n)) +
#   theme_bw() +
#   theme(axis.text.x=element_text(angle=45, hjust=1))
#
# ames_train %>%
#   group_by(SaleType, OverallQual) %>%
#   tally() %>%
#   filter(n > min_n) %>%
#   ggplot(aes(x=factor(SaleType), y=OverallQual)) +
#   geom_point() +
#   geom_text(color='red',size=4,aes(y=OverallQual+0.2, label=n)) +
#
#   theme_bw() +
#   theme(axis.text.x=element_text(angle=45, hjust=1))
#
# ames %>%
#   group_by(SaleType, OverallCond) %>%
#   tally() %>%
#   filter(n > min_n) %>%
#   ggplot(aes(x=factor(SaleType), y=OverallCond)) +
#   geom_point() +
#   geom_text(color='red',size=4,aes(y=OverallCond+0.2, label=n)) +
#   theme_bw() +
#   theme(axis.text.x=element_text(angle=45, hjust=1))
# 
# ames %>%
#   group_by(GarageType, GarageCond) %>%
#   tally() %>%
#   filter(n > min_n) %>%
#   ggplot(aes(x=factor(GarageType), y=GarageCond)) +
#   geom_point() +
#   geom_text(color='red',size=4,aes(y=GarageCond+0.2, label=n)) +
#   theme_bw() +
#   theme(axis.text.x=element_text(angle=45, hjust=1))

nQ_test = test %>%
  group_by(Neighborhood, OverallQual) %>%
  tally() %>%
  filter(n > min_n) %>%
  ggplot(aes(x=reorder(Neighborhood,OverallQual,max), y=OverallQual)) +
  geom_point() +
  geom_text(color='red',size=4,aes(y=OverallQual+0.2, label=n)) +
  theme_bw() +
  labs(x = 'Neighborhood (Test Set)') +
  theme(axis.text.x=element_text(angle=45, hjust=1))


ggsave(paste0("./Linear Model/nQ_test.png"), nQ_test)

nC_test = test %>%
  group_by(Neighborhood, OverallCond) %>%
  tally() %>%
  filter(n > min_n) %>%
  ggplot(aes(x=reorder(Neighborhood,OverallCond,max), y=OverallCond)) +
  geom_point() +
  geom_text(color='red',size=4,aes(y=OverallCond+0.2, label=n)) +
  theme_bw() +
  labs(x = 'Neighborhood (Test Set)') +
  theme(axis.text.x=element_text(angle=45, hjust=1))

ggsave(paste0("./Linear Model/nC_test.png"), nC_test)

nQ = ames %>%
  group_by(Neighborhood, OverallQual) %>%
  tally() %>%
  filter(n > min_n) %>%
  ggplot(aes(x=reorder(Neighborhood,OverallQual,max), y=OverallQual)) +
  geom_point() +
  geom_text(color='red',size=4,aes(y=OverallQual+0.2, label=n)) +
  theme_bw() +
  labs(x = 'Neighborhood (Training Set)') +
  theme(axis.text.x=element_text(angle=45, hjust=1))


ggsave(paste0("./Linear Model/nQ.png"), nQ)

nC = ames %>%
  group_by(Neighborhood, OverallCond) %>%
  tally() %>%
  filter(n > min_n) %>%
  ggplot(aes(x=reorder(Neighborhood,OverallCond,max), y=OverallCond)) +
  geom_point() +
  geom_text(color='red',size=4,aes(y=OverallCond+0.2, label=n)) +
  theme_bw() +
  labs(x = 'Neighborhood (Training Set)') +
  theme(axis.text.x=element_text(angle=45, hjust=1))

ggsave(paste0("./Linear Model/nC.png"), nC)

ames %>%
  group_by(OverallCond, OverallQual) %>%
  tally() %>%
  filter(n > min_n) %>%
  ggplot(aes(x=OverallCond, y=OverallQual)) +
  geom_point() +
  geom_text(color='red',size=4,aes(y=OverallQual+0.2, label=n)) +
  theme_bw() +
  theme(axis.text.x=element_text(angle=45, hjust=1))

ames %>%
  group_by(OverallQual, PriceRange) %>%
  tally() %>%
  filter(n > min_n) %>%
  ggplot(aes(y=OverallQual, x = PriceRange)) +
  geom_point() +
  geom_text(color='red',size=4,aes(y=OverallQual+0.5, label=n)) +
  theme_bw() +
  theme(axis.text.x=element_text(angle=45, hjust=1))

ames %>%
  group_by(OverallCond, PriceRange) %>%
  tally() %>%
  filter(n > min_n) %>%
  ggplot(aes(y=OverallCond, x = PriceRange)) +
  geom_point() +
  geom_text(color='red',size=4,aes(y=OverallCond+0.5, label=n)) +
  theme_bw() +
  theme(axis.text.x=element_text(angle=45, hjust=1))

#ames$PriceRange =ames$SalePrice %>% cut_number(5, labels=c('Cheap','Lower Middle', 'Middle','Upper Middle','Expensive'))



#ames %>% mutate(PriceRange = ifelse(Neighborhood %in% medians$Neighborhood, factor(medians$PriceRange),0)) %>% select(c(Neighborhood,PriceRange))
#ames = ames %>% left_join(., medians[,c('Neighborhood','PriceRange')], by='Neighborhood')

ames %>% 
  group_by(Neighborhood, PriceRange, SalePrice) %>% 
  summarise(med = median(SalePrice), n = n()) %>% 
  filter(n > min_n) %>% 
  ggplot(aes(x=reorder(Neighborhood,n,max), y = PriceRange)) + 
  geom_point(color='white') +
  geom_text(color='red',size=4,aes(y=PriceRange, label=n)) +
  theme_bw() +
  theme(axis.text.x=element_text(angle=45, hjust=1)) 



ames %>% 
  ggplot(aes(x=PriceRange, y = SalePrice)) + 
  geom_boxplot() +
  theme_bw() +
  theme(axis.text.x=element_text(angle=45, hjust=1)) 
ames %>%
  group_by(Neighborhood, ExterCond) %>%
  tally() %>%
  filter(n > 20) %>%
  ggplot(aes(x=Neighborhood, y=ExterCond)) +
  geom_point() +
  geom_text(color='red',size=4,aes(y=ExterCond+0.2, label=n)) +
  theme_bw() +
  theme(axis.text.x=element_text(angle=45, hjust=1))

test %>%
  group_by(ExterQual, ExterCond) %>%
  tally() %>%
  ggplot(aes(x=ExterQual, y=ExterCond)) +
  geom_point() +
  geom_text(color='red',size=4,aes(y=ExterCond+0.2, label=n)) +
  theme_bw() +
  theme(axis.text.x=element_text(angle=45, hjust=1))

test %>%
  group_by(Neighborhood, ExterQual) %>%
  tally() %>%
  filter(n > 20) %>%
  ggplot(aes(x=reorder(Neighborhood,ExterQual,max), y=ExterQual)) +
  geom_point() +
  geom_text(color='red',size=4,aes(y=ExterQual+0.2, label=n)) +
  theme_bw() +
  theme(axis.text.x=element_text(angle=45, hjust=1))
ames %>% transmute(PopNeigh = ifelse(Neighborhood %in% c('NAmes','CollgCr'),1,0))

stat_box_data <- function(y, upper_limit = max(ames$SalePrice) * 1.15) {
  return( 
    data.frame(
      y = 0.98 * upper_limit,
      label = paste('count\n', length(y), '\n')
    )
  )
}

ames %>% 
  ggplot(aes(x=reorder(SaleType,SalePrice,median), y = SalePrice)) + 
  geom_boxplot() +
  stat_summary(
    fun.data = stat_box_data, 
    geom = "text", 
    hjust = 0.5,
    vjust = 0.9
  ) + 
  theme_bw() +
  labs(x = 'Sale Type', y = 'Sale Price')
  theme(axis.text.x=element_text(angle=45, hjust=1)) 

  
ames %>% 
  ggplot(aes(y = SalePrice, x = OverallQual, col = SaleType)) + 
  geom_point() +
  theme_bw()

ames %>% 
  ggplot(aes(y = SalePrice, x = OverallCond, col = SaleType)) + 
  geom_point() +
  theme_bw()


ames %>% 
  ggplot(aes(x = LotArea, y = OverallQual, col = PriceRange)) + 
  geom_point() +
  theme_bw()

ames %>% 
  ggplot(aes(x = GrLivArea, y = OverallQual, col = PriceRange)) + 
  geom_point() +
  theme_bw()

ames %>% 
  ggplot(aes(x = SecFlrSF, y = OverallQual, col = PriceRange)) + 
  geom_point() +
  theme_bw()
  
  
model.nointeraction = lm(SalePrice ~ ExterCond + sqrt(GarageArea), data=ames_train_)
model.interaction = lm(SalePrice ~ ExterCond:sqrt(GarageArea), data=ames_train_)

anova(model.nointeraction, model.interaction)
test_interaction = function(feature1, feature2) {
  
  model.nointeraction = lm(SalePrice ~ GrLivArea + feature1 + feature2, data=ames_train)
  model.interaction = lm(SalePrice ~ GrLivArea + feature1:feature2, data=ames_train)
  #print(summary(model.interaction))
  print(anova(model.nointeraction, model.interaction))
}
#test_interaction(ames_train$OverallCond, ames_train$Neighborhood)
#test_interaction(ames_train$OverallQual, ames_train$Neighborhood)
a = test_interaction(ames_train$Neighborhood, ames_train$OverallCond)

test_interaction(ames_train$ExterCond, sqrt(ames_train$GarageArea))

Numerical Features

library(ggfortify)
require(gridExtra)
plot_numerical_bands = function(df, x_name, y_name) {
  x = df[,x_name][[1]]
  y = df[,y_name][[1]]
  model = lm(y ~ x, data=df)
  predicted = predict(model)
  
  
  
  g = ggplot(df, aes(x,y)) +
    geom_smooth(method = "lm", se = FALSE, color = "lightgrey") +
    geom_segment(aes(xend = x, yend=predicted), alpha=0.2) +
    geom_point(aes(color=abs(model$residuals))) +
    scale_color_continuous(low = "black", high = "red") +
    guides(color=FALSE) +
    geom_point(aes(y=predicted),shape=1) +
    labs(x = x_name, y = y_name) +
    theme_bw()
  
  # Construction of Confidence Intervals
  
  clower = predict(model, interval='confidence', level=0.95)[,2]
  cupper = predict(model, interval='confidence', level=0.95)[,3]
  
  
  plower = predict(model, interval='prediction', level=0.95)[,2]
  pupper = predict(model, interval='prediction', level=0.95)[,3]
  
  g2 = g + geom_ribbon(aes(ymin = clower, ymax = cupper), alpha=0.3, col = 'red') +
    geom_ribbon(aes(ymin=plower, ymax = pupper), alpha=0.2, col='blue')

  model_name = paste0(y_name,'~',x_name)
  dir.create(paste0("./Linear Model/",model_name))
  
  # Save Scatter Plot
  ggsave(paste0("./Linear Model/",model_name,"/scatter_plot.pdf"), g2)
  
  # Save Diagnostic PLots
  pdf(paste0("./Linear Model/",model_name,"/residual_plots.pdf"))
  par(mfrow=c(2,2))
  plot(model,which=c(1,2,4,6))
  dev.off()
  
  # Save Influence Plot
  pdf(paste0("./Linear Model/",model_name,"/influence.pdf"))
  inf = influencePlot(model)

  dev.off()
  
  
  # plot(x, 
  #      abs(model$residuals), 
  #      xlab = x_name, 
  #      ylab = 'Residual Values Squared', 
  #      main = 'Squared Residuals compared to MSE')
  # abline(h=sqrt(mean(model$residuals^2)), lty = 2, lwd=3, col = 'red')
  # ss = smooth.spline(x, model$residuals^2, cv = TRUE)
  # lines(ss, lwd=2, col='blue')
  # abline(h=mean(ss$y), lwd=2, col='black')
  # legend("topleft", 
  #      c("MSE", "Spline", "Spline Mean"),
  #      lty = c(2, 1, 1), 
  #      col = c("red", "blue", "black"))
}
require(gridExtra)
plot_cat_bands = function(df, x_name, y_name) {
  x = df[,x_name][[1]]
  y = df[,y_name][[1]]
  model = lm(y ~ x, data=df)
  predicted = predict(model)
  
  
  
  g = ggplot(df, aes(x,y)) +
    geom_smooth(method = "lm", se = FALSE, color = "lightgrey") +
    geom_segment(aes(xend = x, yend=predicted), alpha=0.2) +
    geom_point(aes(color=abs(model$residuals))) +
    scale_color_continuous(low = "black", high = "red") +
    guides(color=FALSE) +
    geom_point(aes(y=predicted),shape=1) +
    labs(x = x_name, y = y_name) +
    theme_bw()
  
  # Construction of Confidence Intervals
  
  clower = predict(model, interval='confidence', level=0.95)[,2]
  cupper = predict(model, interval='confidence', level=0.95)[,3]
  
  
  plower = predict(model, interval='prediction', level=0.95)[,2]
  pupper = predict(model, interval='prediction', level=0.95)[,3]
  
  g2 = g + geom_ribbon(aes(ymin = clower, ymax = cupper), alpha=0.3, col = 'red') +
    geom_ribbon(aes(ymin=plower, ymax = pupper), alpha=0.2, col='blue')

  model_name = paste0(y_name,'~',x_name)
  dir.create(paste0("./Linear Model/",model_name))
  
  # Save Scatter Plot
  ggsave(paste0("./Linear Model/",model_name,"/scatter_plot.pdf"), g2)
  
  # Save Diagnostic PLots
  pdf(paste0("./Linear Model/",model_name,"/residual_plots.pdf"))
  par(mfrow=c(2,2))
  plot(model,which=c(1,2,4,6))
  dev.off()
  
  # Save Influence Plot
  pdf(paste0("./Linear Model/",model_name,"/influence.pdf"))
  inf = influencePlot(model)

  dev.off()
  
  
  # plot(x, 
  #      abs(model$residuals), 
  #      xlab = x_name, 
  #      ylab = 'Residual Values Squared', 
  #      main = 'Squared Residuals compared to MSE')
  # abline(h=sqrt(mean(model$residuals^2)), lty = 2, lwd=3, col = 'red')
  # ss = smooth.spline(x, model$residuals^2, cv = TRUE)
  # lines(ss, lwd=2, col='blue')
  # abline(h=mean(ss$y), lwd=2, col='black')
  # legend("topleft", 
  #      c("MSE", "Spline", "Spline Mean"),
  #      lty = c(2, 1, 1), 
  #      col = c("red", "blue", "black"))
}

sf_bins = rbin_manual(ames_train, SalePrice, TotalSF, c(1e2,1e3,1e4,1e5,1e6))

plot(sf_bins)
ames_train %>% 
  mutate(BedroomAbvGr = as.factor(BedroomAbvGr)) %>% 
  group_by(BedroomAbvGr) %>% 
  ggplot(aes(x = GrLivArea, y = SalePrice)) + geom_point() + 
  geom_smooth(method = "lm", se = TRUE, color = "red") +
  facet_wrap(BedroomAbvGr~.)
  
feature = 'Alley'
ames_train %>% 
  mutate(feature = as.factor(feature)) %>% 
  group_by(feature) %>% 
  ggplot(aes(x = TotalSF, y = SalePrice)) + geom_point() + 
  geom_smooth(method = "lm", se = TRUE, color = "red") +
  #scale_y_continuous(trans='log') +
  facet_wrap(feature)

feature = 'Alley'
ames_train %>% 
  #mutate(feature = as.factor(feature)) %>% 
  group_by(Alley) %>% 
  summarise(SalePrice = mean(SalePrice)) %>% 
  ggplot(aes(x = Alley, y = SalePrice)) + geom_col()
  #geom_smooth(method = "lm", se = TRUE, color = "red") +
  #scale_y_continuous(trans='log') +
  #facet_wrap(feature)
library(scales)
plot_pred_by_nom = function(nom_feature, predictor, scale_log = FALSE) {
  
  # x = ames_train[,predictor][[1]]
  # #x2 = ames_train[,nom_feature][[1]]
  # model = lm(ames_train$SalePrice ~ x)
  # predicted = predict(model)
  
  g = ames_train %>% 
    mutate(nom_feature = as.factor(nom_feature)) %>% 
    group_by(nom_feature) %>% 
    ggplot(aes_string(x = predictor, y = 'SalePrice'), environment=environment()) + 
    geom_point(shape=1,alpha=0.4) + 
    geom_smooth(method = "lm", se = TRUE, color = "red", linetype='dashed') +
    # geom_segment(aes(xend = x, yend=predicted), alpha=0.2) +
    # geom_point(aes(color=abs(model$residuals))) +
    # scale_color_continuous(low = "black", high = "red") +
    # guides(color=FALSE) +
    # geom_point(aes(y=predicted),shape=1, alpha=0.2) +
    facet_wrap(nom_feature) +
    labs(title = paste0(predictor,' v Sale Price for given ',nom_feature)) +
    theme_bw()
  
  
  model_name = paste0('SalePrice~',predictor)
  if(scale_log){
    g = g + 
      scale_y_continuous(trans='log') + 
      labs(title=paste0(predictor,' v Log Sale Price for given ',nom_feature))
    
    model_name = paste0('Log_SalePrice~',predictor)
  }
  
  file_name = paste0("./Linear Model/Ordinals/",nom_feature,'/',model_name,'.pdf')
  dir.create(paste0("./Linear Model/Ordinals/",nom_feature))
  # Save Scatter Plot
  ggsave(file_name, g)
  #dev.off()
  print(g)
  
}
plot_pred_by_nom('MSSubClass',"TotalSF", scale_log=F)
feature = 'HasPool'
ames_train %>% 
  mutate(feature = as.factor(feature)) %>% 
  #group_by(feature) %>% 
  ggplot(aes(x = TotalSF, y = SalePrice)) + geom_point() + 
  geom_smooth(method = "lm", se = TRUE, color = "red") +
  facet_grid(feature) +
  scale_y_continuous(trans='log')
feature = 'HasGarage'
ames_train %>% 
  mutate(feature = as.factor(feature)) %>% 
  group_by(feature) %>% 
  ggplot(aes(x = TotalSF, y = SalePrice)) + geom_point() + 
  geom_smooth(method = "lm", se = FALSE, color = "red") +
  facet_wrap(feature) +
  scale_y_continuous(trans='log')

boxplot(log(ames_train$SalePrice) ~ ames_train$MoSold, outline = FALSE)
boxplot(ames_train$SalePrice ~ ames_train$YrSold, outline = FALSE)
boxplot(log(ames_train$SalePrice) ~ ames_train$YearBuilt, outline = FALSE)
feature = 'BsmtExposure'
ames_train %>% 
  mutate(feature = as.factor(feature)) %>% 
  group_by(feature) %>% 
  ggplot(aes(x = TotalSF, y = SalePrice)) + geom_point() + 
  geom_smooth(method = "lm", se = FALSE, color = "red") +
  scale_y_continuous(trans='log') +
  facet_wrap(feature)

Lets look at Total square footage across different defined sub classes

feature = 'MSSubClass'
g = ames_train %>% 
  #filter(feature < 70.0) %>% 
  mutate(feature = as.factor(feature)) %>% 
  group_by(feature) %>% 
  ggplot(aes(x = TotalSF, y = SalePrice)) + geom_point() + 
  geom_smooth(method = "lm", se = FALSE, color = "red") +
  scale_y_continuous(trans='log') +
  facet_wrap(feature)


ggsave("./Linear Model/MsSubClass_logplot.pdf", g)
feature = 'MSSubClass'
ames_train %>% 
  filter(AfterWW2==1) %>% 
  mutate(feature = as.factor(feature)) %>% 
  group_by(feature) %>% 
  ggplot(aes(x = GrLivArea, y = SalePrice)) + geom_point() + 
  geom_smooth(method = "lm", se = TRUE, color = "red") +
  #geom_spline(color = "blue") +
  #scale_y_continuous(trans='log') +
  facet_wrap(feature)

ggplot(aes(x, abs(model$residuals)), 
     xlab = x_name, 
     ylab = 'Residual Values Squared', 
     main = 'Squared Residuals compared to MSE')
abline(h=sqrt(mean(model$residuals^2)), lty = 2, lwd=3, col = 'red')
ss = smooth.spline(x, model$residuals^2, cv = TRUE)
lines(ss, lwd=2, col='blue')
abline(h=mean(ss$y), lwd=2, col='black')
model <- eval(bquote(lm(.(f), data = ames_train)))
library(broom)

# Steps 1 and 2
d <- lm(mpg ~ hp, data = mtcars) %>% 
       augment()

head(d)

# Steps 3 and 4
ggplot(d, aes(x = hp, y = mpg)) +
  geom_smooth(method = "lm", se = FALSE, color = "lightgrey") +
  geom_segment(aes(xend = hp, yend = .fitted), alpha = .2) +  # Note `.fitted`
  geom_point(aes(alpha = abs(.resid))) +  # Note `.resid`
  guides(alpha = FALSE) +
  geom_point(aes(y = .fitted), shape = 1) +  # Note `.fitted`
  theme_bw()

# The new line of code


model = lm(log(SalePrice) ~ log(TotalSF), data=ames_train)
predicted = predict(model)

g = ames_train %>% 
  mutate(SalePrice = log(SalePrice), TotalSF = log(TotalSF)) %>% 
  ggplot(aes(x=TotalSF,y=SalePrice)) +
  #geom_point() +
  geom_smooth(method = "lm", se = FALSE, color = "lightgrey") +
  geom_segment(aes(xend = TotalSF, yend=predicted), alpha=0.2) +
  geom_point(aes(color=abs(model$residuals))) +
  scale_color_continuous(low = "black", high = "red") +
  guides(color=FALSE) +
  geom_point(aes(y=predicted),shape=1) +
  theme_bw()

# Construction of Confidence Intervals

clower = predict(model, interval='confidence', level=0.95)[,2]
cupper = predict(model, interval='confidence', level=0.95)[,3]


plower = predict(model, interval='prediction', level=0.95)[,2]
pupper = predict(model, interval='prediction', level=0.95)[,3]

g + geom_ribbon(aes(ymin = clower, ymax = cupper), alpha=0.3, col = 'red') +
  geom_ribbon(aes(ymin=plower, ymax = pupper), alpha=0.2, col='blue')

Definitely a logarithimic relationship…

qqnorm(model$residuals)
qqline(model$residuals)
ames_train %>% 
  ggplot(aes(x = TotalSF, y = SalePrice)) +
  #geom_point() +
  geom_hex(bins=55)

Automating Variable Selection Process

According to Akaike Criterion

library(MASS)
model.empty = mgcv::gam(log(SalePrice) ~ 1, data = ames) #The model with an intercept ONLY.
model.full = ames.gam #The model with ALL variables.
scope = list(lower = formula(model.empty), upper = formula(model.full))
forwardAIC = step(model.empty, scope, direction = "forward", k = 2)
backwardAIC = step(model.full, scope, direction = "backward", k = 2)
bothAIC.empty = step(model.empty, scope, direction = "both", k = 2)
bothAIC.full = step(model.full, scope, direction = "both", k = 2)
summary(bothAIC.empty)
library(mgcv)
set.seed(0);n <- 400
dat <- gamSim(1,n=n,scale=2)
attach(dat)
## Note the increased gamma parameter below to favour
## slightly smoother models...
b<-mgcv::gam(y~ s(x0,bs="ts")+
               s(x1,bs="ts")+
               s(x2,bs="ts")+
               s(x3,bs="ts"),
             gamma=1.4)
summary(b)
plot(b,pages=1)

## Same again using REML/ML
b<-gam(y~s(x0,bs="ts")+s(x1,bs="ts")+s(x2,bs="ts")+
   s(x3,bs="ts")+s(x4,bs="ts")+s(x5,bs="ts"),method="REML")
summary(b)
plot(b,pages=1)

## And once more, but using the null space penalization
b<-gam(y~s(x0,bs="cr")+s(x1,bs="cr")+s(x2,bs="cr")+
   s(x3,bs="cr")+s(x4,bs="cr")+s(x5,bs="cr"),
   method="REML",select=TRUE)
summary(b)
plot(b,pages=1)


detach(dat);rm(dat)
influencePlot(bothAIC.full)

Prediction Function

make_pred = function(model, data) {
  data = data[,attr(model$terms, 'term.labels')]
  pred.band = predict(model, data, interval='prediction')
}
sapply(num_train[rownames(inf),attr(bothAIC.full$terms, 'term.labels')],exp) %>% data.frame()
summary(bothAIC.empty)

Discrete Numerical Features EDA

features = c('YearBuilt','YearRemodAdd','BsmtFullBath','BsmtHalfBath','Full','SalePrice')

train_dis = train[,features]
#test_dis = test[,features]
clean_data_str = function(date_str) {
  mdy = mdy(date_str)
  ymd = ymd(date_str)
  
  if(is.na(mdy)){
    if(is.na(ymd)){
      return(NA)
    }
    else {return(ymd)}
  }
  else {return(mdy)}
}
train_dis %>% ggplot(aes(x = YearBuilt, y = SalePrice)) + geom_point()

Split into test and train

library(tidyverse)

x = model.matrix(SalePrice ~ ., ames)[, -1]
y = ames$SalePrice


set.seed(0)
train = sample(1:nrow(x), 7*nrow(x)/10)
y.test = y[-train]
library(caret)
set.seed(0)
grid = 10^seq(5, -2, length = 100)

cv.lasso.out = cv.glmnet(x[train,],y[train], lambda=grid, alpha=1, nfolds=10)
plot(cv.lasso.out)
bestlambda.lasso = cv.lasso.out$lambda.min
bestlambda.lasso
log(bestlambda.lasso)
lasso.bestlambdatrain = predict(lasso.models.train, s = bestlambda.lasso, newx = x[-train,])

mean((lasso.bestlambdatrain - y.test)^2)
tmp_coeffs <- coef(cv.glmnet.fit, s = "lambda.min")
data.frame(name = tmp_coeffs@Dimnames[[1]][tmp_coeffs@i + 1], coefficient = tmp_coeffs@x)
coefs.lasso = coef(cv.lasso.out)
str(coefs.lasso)
coefs.lasso@x
gam1 = mgcv::gam(SalePrice ~ s(TotalSF, bs='ps', sp=0.6) + s(KitchenAbvGr, bs='ps', sp=0.6), data=ames_train)
### GAM example using mgcv

library(mgcv)
library(ggplot2)
# fake data
n <- 50
sig <- 2
dat <- gamSim(1,n=n,scale=sig)

# P-spline smoothers (with lambda=0.6) used for x1 and x2; x3 is parametric.
b1 <- mgcv::gam(y ~ s(x1, bs='ps', sp=0.6) + s(x2, bs='ps', sp=0.6) + x3, data = dat)
summary(b1)
plot(b1)


# plot the smooth predictor function for x1 with ggplot to get a nicer looking graph
p <- predict(b1, type="lpmatrix")
beta <- coef(b1)[grepl("x1", names(coef(b1)))]
s <- p[,grepl("x1", colnames(p))] %*% beta
ggplot(data=cbind.data.frame(s, dat$x1), aes(x=dat$x1, y=s)) + geom_line()


# predict
newdf <- gamSim(1,n=n,scale=sig)
f <- predict(b1, newdata=newdf)


# select smoothing parameters with REML, using P-splines
b2 <- mgcv::gam(y ~ s(x1, bs='ps') + s(x2, bs='ps') + x3, data = dat, method="REML")

# select variables and smoothing parameters
b3 <- mgcv::gam(y ~ s(x0) + s(x1) + s(x2) + s(x3) , data = dat, method="REML", select=TRUE)

# loess smoothers with the gam package (restart R before loading gam)
library(gam)
b4 <- gam::gam(y ~ lo(x1, span=0.6) + lo(x2, span=0.6) + x3, data = dat)
summary(b4)
feature = 'OverallQual'

df = ames_train %>% group_by(AfterWW2,HouseStyle) %>% summarise(price_med = median(SalePrice), n =n())

ames_train %>% 
  ggplot(aes(x = factor(HouseStyle), y = SalePrice)) + geom_boxplot() +
  geom_text(data=df, color='red',size=4,aes(y=price_med*0.8, label=n)) +
  facet_grid(.~factor(AfterWW2)) +
  labs() +
  #scale_y_continuous(trans='log') +
  theme_bw()
model.fire = lm(log(SalePrice) ~ sqrt(GrLivArea) + sqrt(TotalBsmtSF) + OverallCond + AfterWW2 + Fireplaces + IsNew + OverallQual, data = ames)
summary(model.fire)
plot(model.fire)
inf = influencePlot(model.fire)
ames[rownames(inf),]
model = lm(log(SalePrice) ~ TotalBsmtSF + GrLivArea, data = ames)
 # Create a vector of gradually-changing colors, with one entry for each data # point
the.colors <- rainbow(n = nrow(df))
# For each data point, see how it ranks according to X2, from smallest (1)
# to largest
the.ranks <- rank(ames$GrLivArea)
# Plot residuals vs. X1, colored according to X2 Defining the color and rank
# vectors makes this next line a bit less mysterious, but it's not
# necessary; this could all be a one-liner.
plot(ames$TotalBsmtSF, residuals(model), pch = 19, col = the.colors[the.ranks], ylab = "Residuals")
hist(sqrt(ames$TotalBsmtSF))
hist(ames$TotalBsmtSF)
hist(log(ames$SalePrice))
library(tidyverse)
model.test = lm(SalePrice ~ TotalSF + AfterWW2, data = ames)
res = data.frame(cbind(ames$AfterWW2, residuals(model.test)))
colnames(res) = c('AfterWW2','Residuals')
ggplot(data = res, aes(x = factor(AfterWW2), y = Residuals)) + geom_boxplot()
ggplot(data = res, aes(x=Residuals)) + geom_density(alpha=.2, fill="#FF6666") + facet_grid(res$AfterWW2, scales = 'free_y')
ames %>% 
  filter(YearBuilt > 1990) %>% 
  group_by(YearBuilt,CentralAir) %>% 
  tally() %>% 
  ggplot(aes(x = YearBuilt, y=n, fill=CentralAir)) + geom_col()
ames %>% 
  filter(YearBuilt > 1990) %>% 
  group_by(YearBuilt,MSSubClass) %>% 
  tally() %>% 
  ggplot(aes(x = YearBuilt, y=n, fill=factor(MSSubClass))) + geom_col()
hist(ames$SalePrice)
b = c(-Inf, 1.2e5, 2e5,5e5,Inf)
names = c('Cheap','Average','Expensive','Very Expensive')
bins = cut(ames$SalePrice, breaks = b, labels = names)

ames %>% 
  cbind(., bins) %>% 
  filter(YearBuilt > 1930) %>% 
  group_by(YearBuilt,bins) %>% 
  tally() %>% 
  ggplot(aes(x = YearBuilt, y=n, fill=factor(bins))) + geom_col()
ames %>% 
  group_by(YrSold,YearBuilt) %>% 
  tally() %>% 
  ggplot(aes(x = YrSold, y=n, fill=YearBuilt)) + geom_col(position='fill')
model.test = lm(log(SalePrice) ~ sqrt(TotalSF)*MSSubClass,data=ames )
summary(model.test)
library(AppliedPredictiveModeling)
transparentTheme(trans = .4)
library(caret)
plotSubset <- data.frame(scale(ames[, c("SalePrice", "MSSubClass")])) 
xyplot(SalePrice ~ MSSubClass,
       data = ames,
       groups = ames$AfterWW2, 
       auto.key = list(columns = 2)) 
transformed <- spatialSign(plotSubset)
transformed <- as.data.frame(transformed)
xyplot(SalePrice ~ MSSubClass, 
       data = transformed, 
       groups = ames$AfterWW2, 
       auto.key = list(columns = 2)) 
ames_train %>% 
  group_by(MSSubClass,AfterWW2) %>% 
  ggplot(aes(x = factor(MSSubClass), y=SalePrice)) + geom_boxplot() +facet_grid(('AfterWW2'))

ames_train %>% 
  group_by(MSSubClass,HouseStyle) %>% 
  ggplot(aes(x = factor(MSSubClass), y=SalePrice)) + geom_boxplot() +facet_wrap(('HouseStyle'))
d.tree = rpart::rpart(formula = SalePrice ~ Neighborhood, data = ames)
rpart.plot::rpart.plot(d.tree)
library(tree.bins)
library(rpart)
sample.df <- ames[,c('Neighborhood','SalePrice','MSZoning')]
binned.df <- tree.bins(data = sample.df, y = SalePrice)#, bin.nm = "bin.", return = "new.fctrs")
unique(binned.df$Neighborhood)#current levels of Neighborhood
sample.df <- tree.bins::AmesImpFctrs[, c("Neighborhood", "MS.Zoning", "SalePrice")]
binned.df = tree.bins(data = sample.df, y = SalePrice, bin.nm = "bin.", return = "lkup.list")
unique(binned.df$Neighborhood)#current levels of Neighborhood

ames = ames %>% left_join(., binned.df[[1]][,c('Neighborhood','Categories')], by='Neighborhood')
ames = ames %>% left_join(., binned.df[[2]][,c('MS.Zoning','Categories')], by=c('MSZoning'='MS.Zoning'))

test[!complete.cases(test),]
sum(is.na(test))
#test.binn = test %>% select(c('Neighborhood',''))
test.binn = kknn(PriceRange ~ 
                   Neighborhood + 
                   TotalBathrooms + 
                   KitchenAbvGr +
                   OverallQual +
                   ExterQual +
                   MSZoning, complete, missing, k=1)


ames_noSale = ames %>% select(-SalePrice)
all = rbind(ames_noSale, test)
imputed_Data <- mice(test, m=5, maxit = 20, method = 'pmm', seed = 500);
completeData = complete(imputed_Data,1)
test.comp = tibble(completeData)
test.comp['preds'] = gam.base.predictions
test.comp %>% 
  ggplot(aes(x = 1:nrow(test.comp), y = preds, col = PriceRange)) + geom_point()
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGg9MjIsIGZpZy5oZWlnaHQ9MjApIApsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShjYXJldCkKbGlicmFyeShtZ2N2KQoKCmxpYnJhcnkodmlzcmVnKQoKCmBgYAoKIyMgSW1wb3J0CkltcG9ydGluZyBDbGVhbmVkIERhdGEKYGBge3J9CmFtZXMgPSByZWFkX2NzdignLi9kYXRhL3RyYWluX2NsZWFuZXN0LmNzdicpCnRlc3QgPSByZWFkX2NzdignLi9kYXRhL3Rlc3RfY2xlYW5lc3QuY3N2JykKCgphbWVzID0gYW1lc1ssb3JkZXIoY29sbmFtZXMoYW1lcykpXQp0ZXN0PSB0ZXN0WyxvcmRlcihjb2xuYW1lcyh0ZXN0KSldCgphbWVzID0gYW1lcyAlPiUgZHBseXI6OnNlbGVjdCgtWDEpCnRlc3QgPSB0ZXN0ICU+JSBkcGx5cjo6c2VsZWN0KC1YMSkKCmFtZXMgPSBhbWVzWyxvcmRlcihjb2xuYW1lcyhhbWVzKSldICU+JSAKICByZW5hbWUoJ0ZpcnN0RmxyU0YnID0gIjFzdEZsclNGIiwgJ1NlY0ZsclNGJyA9ICcybmRGbHJTRicsICdUaHJlZVNlYVBvcmNoJyA9ICczU3NuUG9yY2gnKQoKCnRlc3QgPSB0ZXN0WyxvcmRlcihjb2xuYW1lcyh0ZXN0KSldICU+JSAKICByZW5hbWUoJ0ZpcnN0RmxyU0YnID0gIjFzdEZsclNGIiwgJ1NlY0ZsclNGJyA9ICcybmRGbHJTRicsICdUaHJlZVNlYVBvcmNoJyA9ICczU3NuUG9yY2gnKQoKYW1lcyRQcmljZVJhbmdlIDwtIGZhY3RvcihhbWVzJFByaWNlUmFuZ2UsIGxldmVscyA9IGMoIkhpZ2giLCAiTWlkZGxlIiwgIkxvdyIpKQoKdGVzdCRQcmljZVJhbmdlIDwtIGZhY3Rvcih0ZXN0JFByaWNlUmFuZ2UsIGxldmVscyA9IGMoIkhpZ2giLCAiTWlkZGxlIiwgIkxvdyIpKQoKYW1lcyRRdWFsQ29uZCA9IGFtZXMkT3ZlcmFsbENvbmQgKiBhbWVzJE92ZXJhbGxRdWFsCgp0ZXN0JFF1YWxDb25kID0gdGVzdCRPdmVyYWxsQ29uZCAqIHRlc3QkT3ZlcmFsbFF1YWwKYGBgCgojIyBTcGxpdCBpbnRvIHRyYWluIGFuZCB2YWxpZGF0aW9uCmBgYHtyfQpzZXQuc2VlZCgzKQp0cmFpbi5pZHggPSBzYW1wbGUoMTpucm93KGFtZXMpLCA4Km5yb3coYW1lcykvMTApCmFtZXNfdHJhaW4gPSBhbWVzW3RyYWluLmlkeCxdCmFtZXNfdGVzdCA9IGFtZXNbLXRyYWluLmlkeCxdCmBgYAoKYGBge3J9CiMjIEJhc2UgR0FNIE1vZGVsCmFtZXMuZ2FtLmJhc2UgPC0gbWdjdjo6Z2FtKGxvZ1ByaWNlCiAgICAgICAgICAgICAgICB+IHMoVG90YWxTRiwgYnk9UHJpY2VSYW5nZSkKICAgICAgICAgICAgICAgICsgcyhRdWFsQ29uZCwgYnk9IFByaWNlUmFuZ2UpCiAgICAgICAgICAgICAgICArIHMoQWdlKQogICAgICAgICAgICAgICAgKyBGaXJlcGxhY2VzOlByaWNlUmFuZ2UKICAgICAgICAgICAgICAgICsgcyhNU1N1YkNsYXNzKQogICAgICAgICAgICAgICAgKyBOZWlnaGJvcmhvb2Q6UHJpY2VSYW5nZQogICAgICAgICAgICAgICAgKyBzKEdhcmFnZUFyZWEsIGJ5PUdhcmFnZUNhcnMpLAogICAgICAgICAgICAgICAgbWV0aG9kPSdHQ1YuQ3AnLAogICAgICAgICAgICAgICAgZGF0YT1hbWVzKQoKIyMgQmFzZSBMTSBNb2RlbAphbWVzLmxtIDwtIGxtKGxvZ1ByaWNlCiAgICAgICAgICAgICAgfiBUb3RhbFNGOlByaWNlUmFuZ2UKICAgICAgICAgICAgICArIFF1YWxDb25kCiAgICAgICAgICAgICAgKyBBZ2UKICAgICAgICAgICAgICArIEZpcmVwbGFjZXM6UHJpY2VSYW5nZQogICAgICAgICAgICAgICsgTVNTdWJDbGFzcwogICAgICAgICAgICAgICsgTmVpZ2hib3Job29kOlByaWNlUmFuZ2UKICAgICAgICAgICAgICArIEdhcmFnZUFyZWE6R2FyYWdlQ2FycywKICAgICAgICAgICAgICBkYXRhPWFtZXMpCgp0cnVlID0gYW1lc190ZXN0WywnU2FsZVByaWNlJ11bWzFdXQoKIyBnYW0ucHJlZGljdGlvbnMgPSBwcmVkaWN0LmdhbShhbWVzLmdhbSwgbmV3ZGF0YSA9IGFtZXNfdGVzdCwgdHlwZSA9ICdyZXNwb25zZScpCiMgZ2FtLmVycm9ycyA9IGdhbS5wcmVkaWN0aW9ucyAtIGxvZyh0cnVlKQojIGdhbS5kaWZmID1leHAoZ2FtLnByZWRpY3Rpb25zKSAtIHRydWUKCmdhbS5iYXNlLnByZWRpY3Rpb25zID0gcHJlZGljdC5nYW0oYW1lcy5nYW0uYmFzZSwgbmV3ZGF0YSA9IGFtZXNfdGVzdCwgdHlwZSA9ICdyZXNwb25zZScpICNsb2cgcHJlZHMKZ2FtLmJhc2UuZXJyb3JzID0gZ2FtLmJhc2UucHJlZGljdGlvbnMgLSBsb2codHJ1ZSkgIyByZXNpZHVhbHMKZ2FtLmJhc2UuZGlmZiA9ZXhwKGdhbS5iYXNlLnByZWRpY3Rpb25zKSAtIHRydWUgIyAkIGRpZmYKCmxtLnByZWRpY3Rpb25zID0gcHJlZGljdChhbWVzLmxtLCBuZXdkYXRhID0gYW1lc190ZXN0KQpsbS5lcnJvcnMgPSBsbS5wcmVkaWN0aW9ucyAtIGxvZyh0cnVlKQpsbS5kaWZmID0gZXhwKGxtLnByZWRpY3Rpb25zKSAtIHRydWUKCnByaW50KHBhc3RlKCdHQU0gUk1TRTonLHNxcnQobWVhbihnYW0uYmFzZS5lcnJvcnNeMikpKSkKcHJpbnQocGFzdGUoJ0xNIFJNU0U6JyxzcXJ0KG1lYW4obG0uZXJyb3JzXjIpKSkpCgpwcmludChwYXN0ZSgnR0FNOiBFcnJvciBpbiAkIGFzIFByZWRpY3RlZDonLHJvdW5kKG1lYW4oYWJzKGdhbS5iYXNlLmRpZmYpKSwyKSkpCnByaW50KHBhc3RlKCdMTTogRXJyb3IgaW4gJCBhcyBQcmVkaWN0ZWQ6Jyxyb3VuZChtZWFuKGFicyhsbS5kaWZmKSksMikpKQoKc3VtbWFyeShhbWVzLmdhbS5iYXNlKQpgYGAKCgpgYGB7cn0KCmFtZXMudGVzdCA9IGFtZXMgJT4lIG11dGF0ZShPcmRNYXN0ZXIgPSBsb2coUXVhbENvbmQqRXh0ZXJDb25kKkV4dGVyUXVhbCkpCmdncGxvdChhbWVzLnRlc3QsYWVzKHg9T3JkTWFzdGVyLCB5PWxvZ1ByaWNlKSkgKyBnZW9tX3BvaW50KCkKYGBgCgpgYGB7cn0Kb3JkLmxtID0gbG0obG9nUHJpY2UgfiBPcmRNYXN0ZXI6UHJpY2VSYW5nZSwgZGF0YT1hbWVzLnRlc3QpCnBsb3Qob3JkLmxtKQppbmZsdWVuY2VQbG90KG9yZC5sbSkKCgpgYGAKCmBgYHtyfQphbWVzLnRlc3QgPSBhbWVzICU+JSBtdXRhdGUoT3JkTWFzdGVyID0gUXVhbENvbmQqRXh0ZXJDb25kKkV4dGVyUXVhbCkKb3JkLmdhbSA9IG1nY3Y6OmdhbShsb2dQcmljZSB+IAogICAgICAgICAgICAgICAgICAgICAgcyhPcmRNYXN0ZXIpCiAgICAgICAgICAgICAgICAgICAgKyBzKFRvdGFsU0YsIGJ5PVByaWNlUmFuZ2UpCiAgICAgICAgICAgICAgICAgICAgKyBOZWlnaGJvcmhvb2QsCiAgICAgICAgICAgICAgICAgICAgZGF0YT1hbWVzLnRlc3QpCnBsb3Qob3JkLmdhbSkKc3VtbWFyeShvcmQuZ2FtKQpgYGAKCgpgYGB7cn0KI3Bsb3QoYW1lcy5sbSkKI2xpYnJhcnkoY2FyKQppbmYgPSBpbmZsdWVuY2VQbG90KGFtZXMubG0pCmFtZXNbcm93bmFtZXMoaW5mKSxdICU+JSBzZWxlY3QoYyhBZ2UsTmVpZ2hib3Job29kLFF1YWxDb25kLFByaWNlUmFuZ2UsVG90YWxTRikpCnJvd25hbWVzKGluZikKYGBgCmBgYHtyfQoKYW1lcy5maWx0ID0gYW1lc1thcy5udW1lcmljKHJvd25hbWVzKGluZikpLF0KYW1lcyAlPiUgYW50aV9qb2luKC4sIGFtZXMuZmlsdCwgYnkgPSBjKCJTZWNGbHJTRiIsIk5laWdoYm9yaG9vZCIsIkZpcnN0RmxyU0YiKSkKYGBgCgpgYGB7cn0KIyMgQmFzZSBHQU0gTW9kZWwKYW1lcy5nYW0uYmFzZSA8LSBtZ2N2OjpnYW0obG9nUHJpY2UKICAgICAgICAgICAgICAgIH4gcyhUb3RhbFNGLCBieT1QcmljZVJhbmdlKQogICAgICAgICAgICAgICAgIysgcyhUb3RhbEJzbXRTRikKICAgICAgICAgICAgICAgICsgcyhRdWFsQ29uZCwgYnk9UHJpY2VSYW5nZSkKICAgICAgICAgICAgICAgICsgcyhBZ2UpCiAgICAgICAgICAgICAgICArIEZpcmVwbGFjZXM6UHJpY2VSYW5nZQogICAgICAgICAgICAgICAgKyBzKE1TU3ViQ2xhc3MpCiAgICAgICAgICAgICAgICAjKyBQcmljZVJhbmdlCiAgICAgICAgICAgICAgICArIE5laWdoYm9yaG9vZDpQcmljZVJhbmdlCiAgICAgICAgICAgICAgICArIHMoR2FyYWdlQXJlYSwgYnk9R2FyYWdlQ2FycyksCiAgICAgICAgICAgICAgICAjKyBGdWxsQmF0aCwKICAgICAgICAgICAgICAgIG1ldGhvZD0nR0NWLkNwJywKICAgICAgICAgICAgICAgIGRhdGE9YW1lcykKCiMjIEJhc2UgTE0gTW9kZWwKYW1lcy5sbSA8LSBsbShsb2dQcmljZQogICAgICAgICAgICAgIH4gVG90YWxTRjpQcmljZVJhbmdlCiAgICAgICAgICAgICAgIysgVG90YWxCc210U0YKICAgICAgICAgICAgICArIFF1YWxDb25kOlByaWNlUmFuZ2UKICAgICAgICAgICAgICArIEFnZQogICAgICAgICAgICAgICsgRmlyZXBsYWNlczpQcmljZVJhbmdlCiAgICAgICAgICAgICAgKyBNU1N1YkNsYXNzCiAgICAgICAgICAgICAgIysgUHJpY2VSYW5nZQogICAgICAgICAgICAgICsgTmVpZ2hib3Job29kOlByaWNlUmFuZ2UKICAgICAgICAgICAgICArIEdhcmFnZUFyZWE6R2FyYWdlQ2FycywKICAgICAgICAgICAgICAjKyBGdWxsQmF0aCwKICAgICAgICAgICAgICAjbWV0aG9kPSdHQ1YuQ1AnLAogICAgICAgICAgICAgIGRhdGE9YW1lcykKCnRydWUgPSBhbWVzX3Rlc3RbLCdTYWxlUHJpY2UnXVtbMV1dCgojIGdhbS5wcmVkaWN0aW9ucyA9IHByZWRpY3QuZ2FtKGFtZXMuZ2FtLCBuZXdkYXRhID0gYW1lc190ZXN0LCB0eXBlID0gJ3Jlc3BvbnNlJykKIyBnYW0uZXJyb3JzID0gZ2FtLnByZWRpY3Rpb25zIC0gbG9nKHRydWUpCiMgZ2FtLmRpZmYgPWV4cChnYW0ucHJlZGljdGlvbnMpIC0gdHJ1ZQoKZ2FtLmJhc2UucHJlZGljdGlvbnMgPSBwcmVkaWN0LmdhbShhbWVzLmdhbS5iYXNlLCBuZXdkYXRhID0gYW1lc190ZXN0LCB0eXBlID0gJ3Jlc3BvbnNlJykKZ2FtLmJhc2UuZXJyb3JzID0gZ2FtLmJhc2UucHJlZGljdGlvbnMgLSBsb2codHJ1ZSkKZ2FtLmJhc2UuZGlmZiA9ZXhwKGdhbS5iYXNlLnByZWRpY3Rpb25zKSAtIHRydWUKCmxtLnByZWRpY3Rpb25zID0gcHJlZGljdChhbWVzLmxtLCBuZXdkYXRhID0gYW1lc190ZXN0KQpsbS5lcnJvcnMgPSBsbS5wcmVkaWN0aW9ucyAtIGxvZyh0cnVlKQpsbS5kaWZmID0gZXhwKGxtLnByZWRpY3Rpb25zKSAtIHRydWUKCnByaW50KHBhc3RlKCdHQU0gUk1TRTonLHNxcnQobWVhbihnYW0uYmFzZS5lcnJvcnNeMikpKSkKcHJpbnQocGFzdGUoJ0xNIFJNU0U6JyxzcXJ0KG1lYW4obG0uZXJyb3JzXjIpKSkpCgpwcmludChwYXN0ZSgnR0FNOiBFcnJvciBpbiAkIGFzIFByZWRpY3RlZDonLHJvdW5kKG1lYW4oYWJzKGdhbS5iYXNlLmRpZmYpKSwyKSkpCnByaW50KHBhc3RlKCdMTTogRXJyb3IgaW4gJCBhcyBQcmVkaWN0ZWQ6Jyxyb3VuZChtZWFuKGFicyhsbS5kaWZmKSksMikpKQoKc3VtbWFyeShhbWVzLmdhbS5iYXNlKQpgYGAKCmBgYHtyfQoKYW1lcyRQcmljZVJhbmdlIDwtIGZhY3RvcihhbWVzJFByaWNlUmFuZ2UsIGxldmVscyA9IGMoIkxvdyIsICJNaWRkbGUiLCAiSGlnaCIpKQoKYW1lcy5nYW0udGVzdCA9IG1nY3Y6OmdhbShsb2dQcmljZSB+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzKE92ZXJhbGxRdWFsLCBieT1QcmljZVJhbmdlLCBicz0nZ3AnKQogICAgICAgICAgICAgICAgICAgICAgICAgICsgcyhUb3RhbFNGLCBieT1QcmljZVJhbmdlLCBicz0nZ3AnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2Q9J01MJywKICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhPWFtZXMpCgoKdGVzdC5wcmVkcz0gdGVzdCAlPiUgbXV0YXRlKGxvZ1ByaWNlID0gcHJlZGljdC5nYW0oYW1lcy5nYW0udGVzdCwgbmV3ZGF0YSA9IHRlc3QsIHR5cGUgPSAncmVzcG9uc2UnKSkKCmdhbV9xdWFsX2VkYSA9IHZpc3JlZyhhbWVzLmdhbS50ZXN0LCAnT3ZlcmFsbFF1YWwnLCBwYXJ0aWFsPVRSVUUsCiAgICAgICAjc2NhbGU9J3Jlc3BvbnNlJywKICAgICAgIGFscGhhPTAuMDUsIGdnPVRSVUUsIAogICAgICAgbGluZT1saXN0KGNvbD0icmVkIiksCiAgICAgICBmaWxsPWxpc3QoZmlsbD0icGluayIpLAogICAgICAgcG9pbnRzPWxpc3Qoc2l6ZT0xLCBwY2g9MSwgYWxwaGE9MC40LCBjb2w9J2JsYWNrJykpICsgCiAgdGhlbWVfYncoKSArCiAgbGFicyh5ID0gJ0xvZyBTYWxlIFByaWNlJywgeCA9ICdPdmVyYWxsIFF1YWxpdHknKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSdsbScsIGNvbG9yPSdibHVlJykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Y29sb3JzKQoKZ2dzYXZlKHBhc3RlMCgiLi9wcmVzZW50YXRpb24vZ2FtX3F1YWxfZWRhLnBuZyIpLCBnYW1fcXVhbF9lZGEpCgpnYW1fcXVhbF9lZGFQID0gdmlzcmVnKGFtZXMuZ2FtLnRlc3QsICdPdmVyYWxsUXVhbCcsIGJ5PSdQcmljZVJhbmdlJywgcGFydGlhbD1UUlVFLAogICAgICAgI3NjYWxlPSdyZXNwb25zZScsCiAgICAgICBhbHBoYT0wLjA1LCBnZz1UUlVFLCAKICAgICAgIGxpbmU9bGlzdChjb2w9InJlZCIpLAogICAgICAgZmlsbD1saXN0KGZpbGw9InBpbmsiKSwKICAgICAgIHBvaW50cz1saXN0KHNpemU9MSwgcGNoPTEsIGFscGhhPTAuNCwgY29sPSdibGFjaycpKSArIAogIHlsaW0oMTAsMTUpICsKICB0aGVtZV9idygpICsKICBsYWJzKHkgPSAnTG9nIFNhbGUgUHJpY2UnLCB4ID0gJ092ZXJhbGwgUXVhbGl0eScpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbG9ycykKCmdnc2F2ZShwYXN0ZTAoIi4vcHJlc2VudGF0aW9uL2dhbV9xdWFsX2VkYVAucG5nIiksIGdhbV9xdWFsX2VkYVApCgp2aXNyZWcoYW1lcy5nYW0udGVzdCwgJ1RvdGFsU0YnLCBwYXJ0aWFsPVRSVUUsCiAgICAgICAjc2NhbGU9J3Jlc3BvbnNlJywKICAgICAgIGFscGhhPTAuMDUsIGdnPVRSVUUsIAogICAgICAgbGluZT1saXN0KGNvbD0icmVkIiksCiAgICAgICBmaWxsPWxpc3QoZmlsbD0icGluayIpLAogICAgICAgcG9pbnRzPWxpc3Qoc2l6ZT0xLCBwY2g9MSwgYWxwaGE9MC40LCBjb2w9J2JsYWNrJykpICsgCiAgdGhlbWVfYncoKSArCiAgbGFicyh5ID0gJ0xvZyBTYWxlIFByaWNlJywgeCA9ICdUb3RhbCBIb21lIFNGJykgKwogIGdlb21fc21vb3RoKG1ldGhvZD0nbG0nLCBjb2xvcj0nYmx1ZScpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbG9ycykKCgp2aXNyZWcoYW1lcy5nYW0udGVzdCwgJ1RvdGFsU0YnLCBieT0nUHJpY2VSYW5nZScsIHBhcnRpYWw9VFJVRSwKICAgICAgICNzY2FsZT0ncmVzcG9uc2UnLAogICAgICAgYWxwaGE9MC4wNSwgZ2c9VFJVRSwgCiAgICAgICBsaW5lPWxpc3QoY29sPSJyZWQiKSwKICAgICAgIGZpbGw9bGlzdChmaWxsPSJwaW5rIiksCiAgICAgICBwb2ludHM9bGlzdChzaXplPTEsIHBjaD0xLCBhbHBoYT0wLjQsIGNvbD0nYmxhY2snKSkgKyAKICB5bGltKDEwLDE1KSArCiAgdGhlbWVfYncoKSArCiAgbGFicyh5ID0gJ0xvZyBTYWxlIFByaWNlJywgeCA9ICdUb3RhbCBIb21lIFNGJykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Y29sb3JzKQpgYGAKCgpgYGB7cn0KI3Zpc3JlZyhhbWVzLmdhbSwgJ01TU3ViQ2xhc3MnLGJ5PSdQcmljZVJhbmdlJywgb3ZlcmxheT1UUlVFKQoKIyBmaXQgPC0gbG0obG9nKFNhbGVQcmljZSkgfiBwb2x5KEdyTGl2QXJlYSwgMikqcG9seShPdmVyYWxsUXVhbCwgMiksIGRhdGE9YW1lcykKIyB2aXNyZWcyZChmaXQsICJHckxpdkFyZWEiLCAiT3ZlcmFsbFF1YWwiKQojIAojIHZpc3JlZyhhbWVzLmdhbSwgJ1RvdGFsQnNtdFNGJyxieT0nSGFzR2FyYWdlJywgb3ZlcmxheT1UUlVFKQoKZ3JsaXYgPSB2aXNyZWcoYW1lcy5nYW0uYmFzZSwgJ1RvdGFsU0YnLCBwYXJ0aWFsPVRSVUUsCiAgICAgICAjc2NhbGU9J3Jlc3BvbnNlJywKICAgICAgIGFscGhhPTAuMDUsIGdnPVRSVUUsIAogICAgICAgbGluZT1saXN0KGNvbD0icmVkIiksCiAgICAgICBmaWxsPWxpc3QoZmlsbD0icGluayIpLAogICAgICAgcG9pbnRzPWxpc3Qoc2l6ZT0xLCBwY2g9MSwgYWxwaGE9MC40LCBjb2w9J2JsYWNrJykpICsgCiAgdGhlbWVfYncoKSArCiAgbGFicyh5ID0gJ0xvZyBTYWxlIFByaWNlJywgeCA9ICdUb3RhbCBIb21lIFNGJykgKwogIGdlb21fc21vb3RoKG1ldGhvZD0nbG0nLCBjb2xvcj0nYmx1ZScpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbG9ycykKCmdnc2F2ZShwYXN0ZTAoIi4vcHJlc2VudGF0aW9uL2dhbV9ncmxpdi5wbmciKSwgZ3JsaXYpCgpncmxpdl9wcmljZSA9IHZpc3JlZyhhbWVzLmdhbS5iYXNlLCAnVG90YWxTRicsIGJ5PSdQcmljZVJhbmdlJywgcGFydGlhbD1UUlVFLAogICAgICAgI3NjYWxlPSdyZXNwb25zZScsCiAgICAgICBhbHBoYT0wLjA1LCBnZz1UUlVFLCAKICAgICAgIGxpbmU9bGlzdChjb2w9InJlZCIpLAogICAgICAgZmlsbD1saXN0KGZpbGw9InBpbmsiKSwKICAgICAgIHBvaW50cz1saXN0KHNpemU9MSwgcGNoPTEsIGFscGhhPTAuNCwgY29sPSdibGFjaycpKSArIAogIHRoZW1lX2J3KCkgKwogIGxhYnMoeSA9ICdMb2cgU2FsZSBQcmljZScsIHggPSAnVG90YWwgSG9tZSBTRicpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbG9ycykKCgpnZ3NhdmUocGFzdGUwKCIuL3ByZXNlbnRhdGlvbi9nYW1fZ3JsaXZfcHJpY2UucG5nIiksIGdybGl2X3ByaWNlKQoKIyBic210ID0gdmlzcmVnKGFtZXMuZ2FtLmJhc2UsICdUb3RhbEJzbXRTRicsIHBhcnRpYWw9VFJVRSwKIyAgICAgICAgI3NjYWxlPSdyZXNwb25zZScsCiMgICAgICAgIGFscGhhPTAuMDUsIGdnPVRSVUUsIAojICAgICAgICBsaW5lPWxpc3QoY29sPSJyZWQiKSwKIyAgICAgICAgZmlsbD1saXN0KGZpbGw9InBpbmsiKSwKIyAgICAgICAgcG9pbnRzPWxpc3Qoc2l6ZT0xLCBwY2g9MSwgYWxwaGE9MC4yLCBjb2w9J2JsYWNrJykpICsgCiMgICB0aGVtZV9idygpICsKIyAgIGxhYnMoeSA9ICdMb2cgU2FsZSBQcmljZScsIHggPSAnVG90YWwgQmFzZW1lbnQgU0YnKSArCiMgICBnZW9tX3Ntb290aChtZXRob2Q9J2xtJywgY29sb3I9J2JsdWUnKSArCiMgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbG9ycykKCmdnc2F2ZShwYXN0ZTAoIi4vcHJlc2VudGF0aW9uL2dhbV9ic210LnBuZyIpLCBic210KQoKbXN1YiA9IHZpc3JlZyhhbWVzLmdhbS5iYXNlLCAnTVNTdWJDbGFzcycsIHBhcnRpYWw9VFJVRSwKICAgICAgICAgICAgICBqaXR0ZXI9VFJVRSwKICAgICAgICAgICAgICBhbHBoYT0wLjA1LCBnZz1UUlVFLCAKICAgICAgICAgICAgICBsaW5lPWxpc3QoY29sPSJyZWQiKSwKICAgICAgICAgICAgICBmaWxsPWxpc3QoZmlsbD0icGluayIpLAogICAgICAgICAgICAgIHBvaW50cz1saXN0KHNpemU9MSwgcGNoPTEsIGFscGhhPTAuMiwgY29sPSdibGFjaycpKSArIAogIHRoZW1lX2J3KCkgKwogIGxhYnMoeSA9ICdMb2cgU2FsZSBQcmljZScsIHggPSAnTVNTdWJDbGFzczogVHlwZSBvZiBEd2VsbGluZycpICsKICBnZW9tX3Ntb290aChtZXRob2Q9J2xtJywgY29sb3I9J2JsdWUnKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jb2xvcnMpCgpnZ3NhdmUocGFzdGUwKCIuL3ByZXNlbnRhdGlvbi9nYW1fbXN1Yi5wbmciKSwgbXN1YikKCiMgY29uZCA9IHZpc3JlZyhhbWVzLmdhbS5iYXNlLCAnT3ZlcmFsbENvbmQnLCBwYXJ0aWFsPVRSVUUsCiMgICAgICAgICNzY2FsZT0ncmVzcG9uc2UnLAojICAgICAgICBhbHBoYT0wLjA1LCBnZz1UUlVFLCAKIyAgICAgICAgICAgICAgIGppdHRlcj1UUlVFLAojICAgICAgICBsaW5lPWxpc3QoY29sPSJyZWQiKSwKIyAgICAgICAgZmlsbD1saXN0KGZpbGw9InBpbmsiKSwKIyAgICAgICAgcG9pbnRzPWxpc3Qoc2l6ZT0xLCBwY2g9MSwgYWxwaGE9MC4yLCBjb2w9J2JsYWNrJykpICsgCiMgICB0aGVtZV9idygpICsKIyAgIGxhYnMoeSA9ICdMb2cgU2FsZSBQcmljZScsIHggPSAnT3ZlcmFsbCBDb25kaXRpb24gb2YgSG9tZScpICsKIyAgIGdlb21fc21vb3RoKG1ldGhvZD0nbG0nLCBjb2xvcj0nYmx1ZScpICsKIyAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Y29sb3JzKQojIAojIAojIGdnc2F2ZShwYXN0ZTAoIi4vcHJlc2VudGF0aW9uL2dhbV9jb25kLnBuZyIpLCBjb25kKQoKZ2FyYWdlID0gdmlzcmVnKGFtZXMuZ2FtLmJhc2UsICdHYXJhZ2VBcmVhJywgYnk9J0dhcmFnZUNhcnMnLCBwYXJ0aWFsPVRSVUUsCiAgICAgICAjc2NhbGU9J3Jlc3BvbnNlJywKICAgICAgIGFscGhhPTAuMDUsIGdnPVRSVUUsIAogICAgICAgbGluZT1saXN0KGNvbD0icmVkIiksCiAgICAgICAgICAgICAgaml0dGVyPVRSVUUsCiAgICAgICBmaWxsPWxpc3QoZmlsbD0icGluayIpLAogICAgICAgcG9pbnRzPWxpc3Qoc2l6ZT0xLCBwY2g9MSwgYWxwaGE9MC4yLCBjb2w9J2JsYWNrJykpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSdsbScsIGNvbG9yPSdibHVlJykgKwogIHRoZW1lX2J3KCkgKwogIGxhYnMoeSA9ICdMb2cgU2FsZSBQcmljZScsIHggPSAnR2FyYWdlIEFyZWEnKQoKZ2dzYXZlKHBhc3RlMCgiLi9wcmVzZW50YXRpb24vZ2FtX2dhcmFnZS5wbmciKSwgZ2FyYWdlKQoKYWdlID0gdmlzcmVnKGFtZXMuZ2FtLmJhc2UsICdBZ2UnLCBwYXJ0aWFsPVRSVUUsCiAgICAgICAjc2NhbGU9J3Jlc3BvbnNlJywKICAgICAgIGFscGhhPTAuMDUsIGdnPVRSVUUsIAogICAgICAgbGluZT1saXN0KGNvbD0icmVkIiksCiAgICAgICBmaWxsPWxpc3QoZmlsbD0icGluayIpLAogICAgICAgcG9pbnRzPWxpc3Qoc2l6ZT0xLCBwY2g9MSwgYWxwaGE9MC4yLCBjb2w9J2JsYWNrJykpICsgCiAgdGhlbWVfYncoKSArCiAgbGFicyh5ID0gJ0xvZyBTYWxlIFByaWNlJywgeCA9ICdBZ2Ugb2YgSG9tZScpICsKICBnZW9tX3Ntb290aChtZXRob2Q9J2xtJywgY29sb3I9J2JsdWUnKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jb2xvcnMpCgogICMgZ2VvbV9zbW9vdGgobWV0aG9kPSdsbScsIGNvbG9yPSdibHVlJykgKwogICMgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jb2xvcnMpCmdnc2F2ZShwYXN0ZTAoIi4vcHJlc2VudGF0aW9uL2dhbV9hZ2UucG5nIiksIGFnZSkKYGBgCmBgYHtyfQojIyBCYXNlIEdBTSBNb2RlbAoKYW1lcy5nYW0udGVzdCA8LSBtZ2N2OjpnYW0obG9nUHJpY2UKICAgICAgICAgICAgICAgIH4gcyhRdWFsQ29uZCwgYnk9UHJpY2VSYW5nZSkKICAgICAgICAgICAgICAgICsgcyhUb3RhbFNGLCBieT1QcmljZVJhbmdlKSwKICAgICAgICAgICAgICAgIG1ldGhvZD0nR0NWLkNwJywKICAgICAgICAgICAgICAgIGRhdGE9YW1lcykKCiMjIEJhc2UgTE0gTW9kZWwKYW1lcy5sbS50ZXN0IDwtIGxtKGxvZ1ByaWNlCiAgICAgICAgICAgICAgfiBRdWFsQ29uZDpQcmljZVJhbmdlCiAgICAgICAgICAgICAgKyBUb3RhbFNGOlByaWNlUmFuZ2UsCiAgICAgICAgICAgICAgZGF0YT1hbWVzKQoKdHJ1ZSA9IGFtZXNfdGVzdFssJ1NhbGVQcmljZSddW1sxXV0KCnN1bW1hcnkoYW1lcy5nYW0udGVzdCkKI3N1bW1hcnkoYW1lcy5sbS50ZXN0KQoKYGBgCmBgYHtyfQp2aXNyZWcoYW1lcy5nYW0uYmFzZSwgIlF1YWxDb25kIiwgcGFydGlhbD1UUlVFLAogICAgICAgI3NjYWxlPSdyZXNwb25zZScsCiAgICAgICAKICAgICAgIGJyZWFrcyA9IGMoMTAwMCwyMDAwLDMwMDApLAogICAgICAgYWxwaGE9MC4wNSwgZ2c9VFJVRSkgCgp2aXNyZWcoYW1lcy5nYW0uYmFzZSwgIlF1YWxDb25kIiwgYnk9IlByaWNlUmFuZ2UiLAogICAgICAgc2NhbGU9J3Jlc3BvbnNlJywKICAgICAgIAogICAgICAgYWxwaGE9MC4wNSwgZ2c9VFJVRSkgICsgeWxpbSgxMCwxNSkKCnZpc3JlZyhhbWVzLmdhbS5iYXNlLCAiUHJpY2VSYW5nZSIsIGJ5PSdUb3RhbFNGJywgcGFydGlhbD1UUlVFLAogICAgICAgc2NhbGU9J3Jlc3BvbnNlJywKICAgICAgIAogICAgICAgb3ZlcmxheT1UUlVFLCBicmVha3MgPSBjKDEwMDAsMjAwMCwzMDAwKSwKICAgICAgIGFscGhhPTAuMDUsIGdnPVRSVUUpIAoKCnZpc3JlZyhhbWVzLmxtLCAiUHJpY2VSYW5nZSIsIGJ5PSdUb3RhbFNGJywgcGFydGlhbD1UUlVFLAogICAgICAgc2NhbGU9J3Jlc3BvbnNlJywKICAgICAgIGJyZWFrcyA9IGMoMTUwMCwyNTAwLDM1MDApLAogICAgICAgYWxwaGE9MC4wMSwgZ2c9VFJVRSkgCgoKdmlzcmVnKGFtZXMubG0sICJUb3RhbFNGIiwgYnk9J1ByaWNlUmFuZ2UnLCBwYXJ0aWFsPVRSVUUsCiAgICAgICBzY2FsZT0ncmVzcG9uc2UnLAogICAgICAgI2JyZWFrcyA9IGMoMTUwMCwyNTAwLDM1MDApLAogICAgICAgYWxwaGE9MC4wMSwgZ2c9VFJVRSkgCgp2aXNyZWcoYW1lcy5nYW0uYmFzZSwgJ1RvdGFsU0YnLHBhcnRpYWw9VFJVRSwKICAgICAgICNzY2FsZT0ncmVzcG9uc2UnLAogICAgICAgYWxwaGE9MC4wNSwgZ2c9VFJVRSwgCiAgICAgICBsaW5lPWxpc3QoY29sPSJyZWQiKSwKICAgICAgIGZpbGw9bGlzdChmaWxsPSJwaW5rIiksCiAgICAgICBwb2ludHM9bGlzdChzaXplPTEsIHBjaD0xLCBhbHBoYT0wLjQsIGNvbD0nYmxhY2snKSkgKyAKICB0aGVtZV9idygpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nKQogIGxhYnMoeSA9ICdMb2cgU2FsZSBQcmljZScsIHggPSAnVG90YWwgSG9tZSBTRicpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbG9ycykKCnZpc3JlZyhhbWVzLmdhbS5iYXNlLCAnVG90YWxTRicsIGJ5PSdQcmljZVJhbmdlJywgcGFydGlhbD1UUlVFLAogICAgICAgI3NjYWxlPSdyZXNwb25zZScsCiAgICAgICBhbHBoYT0wLjA1LCBnZz1UUlVFLCAKICAgICAgIGxpbmU9bGlzdChjb2w9InJlZCIpLAogICAgICAgZmlsbD1saXN0KGZpbGw9InBpbmsiKSwKICAgICAgIHBvaW50cz1saXN0KHNpemU9MSwgcGNoPTEsIGFscGhhPTAuNCwgY29sPSdibGFjaycpKSArIAogIHRoZW1lX2J3KCkgKwogIGxhYnMoeSA9ICdMb2cgU2FsZSBQcmljZScsIHggPSAnVG90YWwgSG9tZSBTRicpICsKICB5bGltKDEwLDE1KSsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbG9ycykKYGBgCgoKYGBge3J9CnZpc3JlZyhhbWVzLmxtLCAiUHJpY2VSYW5nZSIsIGJ5PSJUb3RhbFNGIiwgcGFydGlhbD1UUlVFLAogICAgICAgI3NjYWxlPSdyZXNwb25zZScsCiAgICAgICBvdmVybGF5PVRSVUUsIGJyZWFrcyA9IGMoMTAwMCwyMDAwLDMwMDApLAogICAgICAgYWxwaGE9MC4wNSwgZ2c9VFJVRSkgCgp2aXNyZWcoYW1lcy5nYW0uYmFzZSwgIlByaWNlUmFuZ2UiLCBieT0iVG90YWxTRiIsLCBwYXJ0aWFsPVRSVUUsCiAgICAgICAjc2NhbGU9J3Jlc3BvbnNlJywKICAgICAgIG92ZXJsYXk9VFJVRSwgYnJlYWtzID0gYygxMDAwLDIwMDAsMzAwMCksCiAgICAgICBhbHBoYT0wLjA1LCBnZz1UUlVFKQpgYGAKCmBgYHtyfQoKc3RkID0gc2QoZ2FtLmJhc2UuZXJyb3JzKQpkYXRnYW0gPC0gZGF0YS5mcmFtZShkZW5zaXR5ID0gYyhnYW0uYmFzZS5lcnJvcnMpLCBtb2RlbCA9IHJlcChjKCJHQU0iKSkpCmRhdGxtIDwtIGRhdGEuZnJhbWUoZGVuc2l0eSA9IGMobG0uZXJyb3JzKSwgbW9kZWwgPSByZXAoYygiTE0iKSkpCiMgZGVuc19jb21wID0gZ2dwbG90KGRhdCxhZXMoeCA9IGRlbnNpdHksIGZpbGw9IG1vZGVsKSkgKyAKIyAgICMgc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwgYXJncz1saXN0KDAsc3RkKSwgYWVzKGNvbD0nR2F1c3NpYW4nKSkgKwojICAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNSkgKwojICAgdGhlbWVfYncoKSArCiMgICAjc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iRGFyazIiKSArCiMgICBsYWJzKHg9J1Jlc2lkdWFsIE1hZ25pdHVkZScsIHk9J0ZyZXF1ZW5jeScpCgpkYXRnYW0gJT4lIAogIGdncGxvdChhZXMoeD1kZW5zaXR5KSkgKwogIGdlb21fZGVuc2l0eSgpCgoKZ2dzYXZlKHBhc3RlMCgiLi9wcmVzZW50YXRpb24vZGVuc19jb21wLnBuZyIpLCBkZW5zX2NvbXApCgpwbG90KHRydWUsIGV4cChnYW0uYmFzZS5wcmVkaWN0aW9ucyksIHlsaW09YygwLDdlNSksIHhsaW09YygwLDdlNSkpCmFibGluZShhPTAsYj0xKQoKI3BvaW50cyhhbWVzX3Rlc3RfWywnU2FsZVByaWNlJ11bWzFdXSwgZXhwKGxtLnByZWRpY3Rpb25zKSkKcGxvdCh0cnVlLCBleHAobG0ucHJlZGljdGlvbnMpLCB5bGltPWMoMCw3ZTUpLCB4bGltPWMoMCw3ZTUpKQphYmxpbmUoYT0wLGI9MSkKCmBgYApgYGB7cn0KIyBnYW0uZG9sbGFycyA9IGRhdGEuZnJhbWUoY2JpbmQoZXhwKGdhbS5iYXNlLnByZWRpY3Rpb25zKS10cnVlLCB0cnVlKSkKIyBjb2xuYW1lcyhnYW0uZG9sbGFycykgPSBjKCdFcnJvcicsJ1JlYWwnKQojIAojIGdncGxvdChnYW0uZG9sbGFycywgYWVzKHg9UmVhbCwgeT1FcnJvcikpICsgCiMgICBnZW9tX3BvaW50KCkgKwojICAgdGhlbWVfYncoKSArCiMgICB5bGltKC0xZTUsMWU1KSArIAojICAgeGxpbSgwLDdlNSkgKwojICAgbGFicyh4ID0gJ1RydWUgUHJpY2UgWyRdJyx5ID0gJ1ByZWRpY3Rpb24gRXJyb3IgWyRdJykKIyAKIyBzbG9wZSA9IG1lYW4oYWJzKGdhbS5kb2xsYXJzJEVycm9yKSkvbWVhbihnYW0uZG9sbGFycyRSZWFsKQojICNzbG9wZSA9IG1lYW4oYWJzKGdhbS5kb2xsYXJzJEVycm9yKS9nYW0uZG9sbGFycyRSZWFsKQojIGdncGxvdChnYW0uZG9sbGFycywgYWVzKHg9UmVhbCwgeT1hYnMoRXJyb3IpKSkgKyAKIyAgIGdlb21fcG9pbnQoKSArCiMgICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDMqc2xvcGUsIGxpbmV0eXBlPSdkYXNoZWQnKSArCiMgICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDIqc2xvcGUsIGxpbmV0eXBlPSdkYXNoZWQnKSArCiMgICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEqc2xvcGUsIGxpbmV0eXBlPSdkYXNoZWQnKSArCiMgICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDAqc2xvcGUsIGxpbmV0eXBlPSdkYXNoZWQnKSArCiMgICB0aGVtZV9idygpICsKIyAgICN5bGltKC0xZTUsMWU1KSArIAojICAgeGxpbSgwLDdlNSkgKwojICAgbGFicyh4ID0gJ1RydWUgUHJpY2UgWyRdJyx5ID0gJ0dBTSBBYnNvbHV0ZSBQcmVkaWN0aW9uIEVycm9yIFskXScpCiMgCiMgI2dnc2F2ZShwYXN0ZTAoIi4vcHJlc2VudGF0aW9uL3Jlc19nYW0ucG5nIiksIHJlc19nYW0pCgoKCmdhbS50ZXN0X3ByZWRpY3Rpb25zID0gcHJlZGljdC5nYW0oYW1lcy5nYW0uYmFzZSwgbmV3ZGF0YSA9IGFtZXNfdGVzdCwgdHlwZSA9ICdyZXNwb25zZScpCmxtLnRlc3RfcHJlZGljdGlvbnMgPSBwcmVkaWN0KGFtZXMubG0sIG5ld2RhdGEgPSBhbWVzX3Rlc3QsIHR5cGUgPSAncmVzcG9uc2UnKQoKZ2FtLmRvbGxhcnMgPSBkYXRhLmZyYW1lKGNiaW5kKGdhbS50ZXN0X3ByZWRpY3Rpb25zLCBsb2codHJ1ZSksIGdhbS50ZXN0X3ByZWRpY3Rpb25zLWxvZyh0cnVlKSkpCmNvbG5hbWVzKGdhbS5kb2xsYXJzKSA9IGMoJ0ZpdHRlZCcsJ1JlYWwnLCdFcnJvcicpCmdhbS5kb2xsYXJzJHN0ZEVycm9yID0gZ2FtLmRvbGxhcnMkRXJyb3Ivc2QoZ2FtLmRvbGxhcnMkRXJyb3IpCgpsbS5kb2xsYXJzID0gZGF0YS5mcmFtZShjYmluZChsbS50ZXN0X3ByZWRpY3Rpb25zLCBsb2codHJ1ZSksIGxtLnRlc3RfcHJlZGljdGlvbnMtbG9nKHRydWUpKSkKY29sbmFtZXMobG0uZG9sbGFycykgPSBjKCdGaXR0ZWQnLCdSZWFsJywnRXJyb3InKQpsbS5kb2xsYXJzJHN0ZEVycm9yID0gbG0uZG9sbGFycyRFcnJvci9zZChsbS5kb2xsYXJzJEVycm9yKQogCiNzbG9wZSA9IG1lYW4oYWJzKGdhbS5kb2xsYXJzJEVycm9yKSkvbWVhbihnYW0uZG9sbGFycyRSZWFsKQoKI2dhbV9yZXMgPSAKICBnZ3Bsb3QoZ2FtLmRvbGxhcnMsIGFlcyh4PUZpdHRlZCwgeT1zdGRFcnJvcikpICsgCiAgZ2VvbV9wb2ludChhbHBoYT0wLjIpICsKICBnZW9tX3Ntb290aCgpICsKICB0aGVtZV9idygpICsKICAjeWxpbSgtMWU1LDFlNSkgKyAKICAjeGxpbSgxLGxvZyg3ZTUpKSArCiAgbGFicyh4ID0gJ0dBTSBGaXR0ZWQgVmFsdWVzJyx5ID0gJ1Jlc2lkdWFsIEVycm9yJykKCiNnZ3NhdmUocGFzdGUwKCIuL3ByZXNlbnRhdGlvbi9nYW1fcmVzLnBuZyIpLCBnYW1fcmVzKQoKIAojc2xvcGUgPSBtZWFuKGFicyhnYW0uZG9sbGFycyRFcnJvcikpL21lYW4oZ2FtLmRvbGxhcnMkUmVhbCkKCiNsbV9yZXMgPSAKICBnZ3Bsb3QobG0uZG9sbGFycywgYWVzKHg9Rml0dGVkLCB5PXN0ZEVycm9yKSkgKyAKICBnZW9tX3BvaW50KGFscGhhPTAuMikgKwogIGdlb21fc21vb3RoKCkgKwogIHRoZW1lX2J3KCkgKwogICN5bGltKC0xZTUsMWU1KSArIAogICN4bGltKDEsbG9nKDdlNSkpICsKICBsYWJzKHggPSAnTE0gRml0dGVkIFZhbHVlcycseSA9ICdSZXNpZHVhbCBFcnJvcicpCgojZ2dzYXZlKHBhc3RlMCgiLi9wcmVzZW50YXRpb24vbG1fcmVzLnBuZyIpLCBsbV9yZXMpCnBsb3QoYW1lcy5sbSkKCmBgYAoKIyMgVGVzdGluZyBNb2RlbCBBc3N1bXB0aW9ucwpgYGB7cn0KZ2FtLmFuYWw9IGRhdGEuZnJhbWUoY2JpbmQoYW1lcy5nYW0uYmFzZSRmaXR0ZWQudmFsdWVzLCBhbWVzLmdhbS5iYXNlJHJlc2lkdWFscykpCmNvbG5hbWVzKGdhbS5hbmFsKSA9IGMoJ0ZpdHRlZCcsJ0Vycm9yJykKZ2FtLmFuYWwkc3RkRXJyb3IgPSBhbWVzLmdhbS5iYXNlJHJlc2lkdWFscy9zZChhbWVzLmdhbS5iYXNlJHJlc2lkdWFscykKCmdhbS5hbmFsICU+JSAKICBnZ3Bsb3QoYWVzKHg9Rml0dGVkLCB5PXN0ZEVycm9yKSkgKyAKICBnZW9tX3BvaW50KGFscGhhPTAuMikgKwogIGdlb21fc21vb3RoKCkgKwogIHRoZW1lX2J3KCkgKwogICN5bGltKC0xZTUsMWU1KSArIAogICN4bGltKDEsbG9nKDdlNSkpICsKICBsYWJzKHggPSAnR0FNIEZpdHRlZCBWYWx1ZXMnLHkgPSAnUmVzaWR1YWwgRXJyb3InKQoKCmdhbS5hbmFsICU+JSAKICBnZ3Bsb3QoYWVzKHg9Rml0dGVkLCB5PXN0ZEVycm9yXjIpKSArIAogIGdlb21fcG9pbnQoYWxwaGE9MC4yKSArCiAgZ2VvbV9zbW9vdGgoKSArCiAgdGhlbWVfYncoKSArCiAgI3lsaW0oLTFlNSwxZTUpICsgCiAgI3hsaW0oMSxsb2coN2U1KSkgKwogIGxhYnMoeCA9ICdHQU0gRml0dGVkIFZhbHVlcycseSA9ICdSZXNpZHVhbCBFcnJvcicpCgpnYW0uYW5hbCAlPiUgCiAgZ2dwbG90KGFlcyh4PXN0ZEVycm9yKSkgKwogIGdlb21fZGVuc2l0eSgpCiAgdGhlbWVfYncoKQogIApnYW0uYW5hbCAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gc2FtcGxlKDE6bnJvdyhnYW0uYW5hbCkpLCB5ID0gc3RkRXJyb3IpKSArCiAgZ2VvbV9wb2ludChhbHBoYT0wLjUpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9cXQoMC4wMjUsIGRmID0gbnJvdyhnYW0uYW5hbCkgLSAyKSwgY29sb3IgPSAicmVkIiwgbGluZXR5cGU9ImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9cXQoMSAtIDAuMDI1LCBkZiA9IG5yb3coZ2FtLmFuYWwpIC0gMiksIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlPSJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArCiAgbGFicyh4ID0gJ1RyYWluIFNldCBJbmRleCcsIHkgPSAnR0FNIFN0YW5kYXJkaXplZCBSZXNpZHVhbHMnKSArCiAgdGhlbWVfYncoKQogIAogIAogIApsbS5hbmFsPSBkYXRhLmZyYW1lKGNiaW5kKGFtZXMubG0kZml0dGVkLnZhbHVlcywgYW1lcy5sbSRyZXNpZHVhbHMpKQpjb2xuYW1lcyhsbS5hbmFsKSA9IGMoJ0ZpdHRlZCcsJ0Vycm9yJykKbG0uYW5hbCRzdGRFcnJvciA9IGFtZXMubG0kcmVzaWR1YWxzL3NkKGFtZXMubG0kcmVzaWR1YWxzKQoKbG0uYW5hbCAlPiUgCiAgZ2dwbG90KGFlcyh4PUZpdHRlZCwgeT1zdGRFcnJvcikpICsgCiAgZ2VvbV9wb2ludChhbHBoYT0wLjIpICsKICBnZW9tX3Ntb290aCgpICsKICB0aGVtZV9idygpICsKICBsYWJzKHggPSAnTE0gRml0dGVkIFZhbHVlcycseSA9ICdSZXNpZHVhbCBFcnJvcicpCgpsbS5hbmFsICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBzYW1wbGUoMTpucm93KGxtLmFuYWwpKSwgeSA9IHN0ZEVycm9yKSkgKwogIGdlb21fcG9pbnQoYWxwaGE9MC41KSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PXF0KDAuMDI1LCBkZiA9IG5yb3cobG0uYW5hbCkgLSAyKSwgY29sb3IgPSAicmVkIiwgbGluZXR5cGU9ImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9cXQoMSAtIDAuMDI1LCBkZiA9IG5yb3cobG0uYW5hbCkgLSAyKSwgY29sb3IgPSAicmVkIiwgbGluZXR5cGU9ImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsKICBsYWJzKHggPSAnVHJhaW4gU2V0IEluZGV4JywgeSA9ICdMTSBTdGFuZGFyZGl6ZWQgUmVzaWR1YWxzJykgKwogIHRoZW1lX2J3KCkKYGBgCgoKYGBge3J9CmxtLmRvbGxhcnMgPSBkYXRhLmZyYW1lKGNiaW5kKGV4cChsbS5wcmVkaWN0aW9ucyktdHJ1ZSwgdHJ1ZSkpCmNvbG5hbWVzKGxtLmRvbGxhcnMpID0gYygnRXJyb3InLCdSZWFsJykKCmdncGxvdChsbS5kb2xsYXJzLCBhZXMoeD1SZWFsLCB5PUVycm9yKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHRoZW1lX2J3KCkgKwogIHlsaW0oLTFlNSwxZTUpICsgCiAgeGxpbSgwLDdlNSkgKwogIGxhYnMoeCA9ICdUcnVlIFByaWNlIFskXScseSA9ICdQcmVkaWN0aW9uIEVycm9yIFskXScpCgpzbG9wZSA9IG1lYW4oYWJzKGxtLmRvbGxhcnMkRXJyb3IpKS9tZWFuKGxtLmRvbGxhcnMkUmVhbCkKI3Nsb3BlID0gbWVhbihhYnMoZ2FtLmRvbGxhcnMkRXJyb3IpL2dhbS5kb2xsYXJzJFJlYWwpCnJlc19sbSA9IGdncGxvdChsbS5kb2xsYXJzLCBhZXMoeD1SZWFsLCB5PWFicyhFcnJvcikpKSArIAogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAzKnNsb3BlLCBsaW5ldHlwZT0nZGFzaGVkJykgKwogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMipzbG9wZSwgbGluZXR5cGU9J2Rhc2hlZCcpICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEqc2xvcGUsIGxpbmV0eXBlPSdkYXNoZWQnKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAwKnNsb3BlLCBsaW5ldHlwZT0nZGFzaGVkJykgKwogIHRoZW1lX2J3KCkgKwogICN5bGltKC0xZTUsMWU1KSArIAogIHhsaW0oMCw3ZTUpICsKICBsYWJzKHggPSAnVHJ1ZSBQcmljZSBbJF0nLHkgPSAnTE0gQWJzb2x1dGUgUHJlZGljdGlvbiBFcnJvciBbJF0nKQoKCmdnc2F2ZShwYXN0ZTAoIi4vcHJlc2VudGF0aW9uL3Jlc19sbS5wbmciKSwgcmVzX2xtKQoKbG0uZG9sbGFycyA9IGRhdGEuZnJhbWUoY2JpbmQobG0ucHJlZGljdGlvbnMtbG9nKHRydWUpLCBsb2codHJ1ZSkpKQpjb2xuYW1lcyhsbS5kb2xsYXJzKSA9IGMoJ0Vycm9yJywnUmVhbCcpCgpnZ3Bsb3QobG0uZG9sbGFycywgYWVzKHg9UmVhbCwgeT1FcnJvcikpICsgCiAgZ2VvbV9wb2ludCgpICsKICB0aGVtZV9idygpICsKICAjeWxpbSgtMWU1LDFlNSkgKyAKICAjeGxpbSgwLDdlNSkgKwogIGxhYnMoeCA9ICdMb2cgVHJ1ZSBQcmljZScseSA9ICdMTSBQcmVkaWN0aW9uIEVycm9yJykKCnNsb3BlID0gbWVhbihhYnMobG0uZG9sbGFycyRFcnJvcikpL21lYW4obG0uZG9sbGFycyRSZWFsKQojc2xvcGUgPSBtZWFuKGFicyhnYW0uZG9sbGFycyRFcnJvcikvZ2FtLmRvbGxhcnMkUmVhbCkKbG9nX3Jlc19sbSA9IGdncGxvdChsbS5kb2xsYXJzLCBhZXMoeD1SZWFsLCB5PWFicyhFcnJvcikpKSArIAogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAzKnNsb3BlLCBsaW5ldHlwZT0nZGFzaGVkJykgKwogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMipzbG9wZSwgbGluZXR5cGU9J2Rhc2hlZCcpICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEqc2xvcGUsIGxpbmV0eXBlPSdkYXNoZWQnKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAwKnNsb3BlLCBsaW5ldHlwZT0nZGFzaGVkJykgKwogIHRoZW1lX2J3KCkgKwogICN5bGltKC0xZTUsMWU1KSArIAogICN4bGltKDEsbG9nKDdlNSkpICsKICBsYWJzKHggPSAnTG9nIFRydWUgUHJpY2UnLHkgPSAnTE0gQWJzb2x1dGUgUHJlZGljdGlvbiBFcnJvcicpCgoKZ2dzYXZlKHBhc3RlMCgiLi9wcmVzZW50YXRpb24vbG9nX3Jlc19sbS5wbmciKSwgbG9nX3Jlc19sbSkKYGBgCgoKYGBge3J9CmFtZXMuZ2FtIDwtIG1nY3Y6OmdhbShsb2coU2FsZVByaWNlKQogICAgICAgICAgICAgICAgfiBzKEdyTGl2QXJlYSwgYnk9UHJpY2VSYW5nZSwgYnM9J2NzJywgaWQ9MSkKICAgICAgICAgICAgICAgICsgcyhZZWFyQnVpbHQpCiAgICAgICAgICAgICAgICArIHRpKE92ZXJhbGxRdWFsLE92ZXJhbGxDb25kKQogICAgICAgICAgICAgICAgKyBzKE1TU3ViQ2xhc3MpCiAgICAgICAgICAgICAgICArIHMoWWVhckJ1aWx0KQogICAgICAgICAgICAgICAgKyBDZW50cmFsQWlyCiAgICAgICAgICAgICAgICAsbWV0aG9kPSdHQ1YuQ3AnLCBkYXRhPWFtZXNfdHJhaW4sIGdhbW1hPTEuNCwgc2VsZWN0PVRSVUUpCgp2aXNyZWcoYW1lcy5nYW0pCgp0cnVlID0gYW1lc190ZXN0WywnU2FsZVByaWNlJ11bWzFdXQoKZ2FtLnByZWRpY3Rpb25zID0gcHJlZGljdC5nYW0oYW1lcy5nYW0sIG5ld2RhdGEgPSBhbWVzX3Rlc3QsIHR5cGUgPSAncmVzcG9uc2UnKQpnYW0uZXJyb3JzID0gZ2FtLnByZWRpY3Rpb25zIC0gbG9nKHRydWUpCmdhbS5kaWZmID1leHAoZ2FtLnByZWRpY3Rpb25zKSAtIHRydWUKCnBsb3QoZGVuc2l0eShnYW0uZXJyb3JzKSwgY29sID0ncmVkJykKbGluZXMoZGVuc2l0eShnYW0uYmFzZS5lcnJvcnMpLCBjb2w9J2JsYWNrJywgbHR5PTIpCiNwbG90KGFtZXMuZ2FtKQpsZWdlbmQoInRvcHJpZ2h0IixsZWdlbmQ9YygnR0FNIFJlc2lkdWFscycsJ0Jhc2UgR0FNIFJlc2lkdWFscycpLAogICAgICAgY29sPWMoInJlZCIsImJsYWNrIiksIGx0eT0xOjIsIGNleD0wLjgpCgoKcHJpbnQocGFzdGUoJ0dBTSBSTVNFOicsc3FydChtZWFuKGdhbS5lcnJvcnNeMikpKSkKCnByaW50KHBhc3RlKCdHQU06IEVycm9yIGluICQgYXMgUHJlZGljdGVkOicscm91bmQobWVhbihhYnMoZ2FtLmRpZmYpKSwyKSkpCgojcGxvdChhbWVzX3Rlc3RbLCdTYWxlUHJpY2UnXVtbMV1dLCBnYW0uZXJyb3JzKQoKcGxvdCh0cnVlLCBleHAoZ2FtLnByZWRpY3Rpb25zKSwgeWxpbT1jKDAsN2U1KSwgeGxpbT1jKDAsN2U1KSkKYWJsaW5lKGE9MCxiPTEpCgpzdW1tYXJ5KGFtZXMuZ2FtKQpgYGAKCgpgYGB7cn0KCgpnYW0udGVzdF9wcmVkaWN0aW9ucyA9IHByZWRpY3QuZ2FtKGFtZXMuZ2FtLmJhc2UsIG5ld2RhdGEgPSB0ZXN0LCB0eXBlID0gJ3Jlc3BvbnNlJykKc3VibWlzc2lvbiA9IGRhdGEuZnJhbWUoZXhwKGdhbS50ZXN0X3ByZWRpY3Rpb25zKSkKY29sbmFtZXMoc3VibWlzc2lvbikgPSAnU2FsZVByaWNlJwpzdWJtaXNzaW9uID0gdGliYmxlOjpyb3dpZF90b19jb2x1bW4oc3VibWlzc2lvbiwnSWQnKQpyb3duYW1lcyhzdWJtaXNzaW9uKSA9IDE0NjE6KG5yb3coc3VibWlzc2lvbikrMTQ2MCkKZ21iX3N1Ym1pc3Npb24gPSBtdXRhdGUoc3VibWlzc2lvbiwgSWQgPSBJZCArIDE0NjApCgpsbS50ZXN0X3ByZWRpY3Rpb25zID0gcHJlZGljdChhbWVzLmxtLCBuZXdkYXRhID0gdGVzdCwgdHlwZSA9ICdyZXNwb25zZScpCmxtX3N1Ym1pc3Npb24gPSBkYXRhLmZyYW1lKGV4cChsbS50ZXN0X3ByZWRpY3Rpb25zKSkKY29sbmFtZXMobG1fc3VibWlzc2lvbikgPSAnU2FsZVByaWNlJwpsbV9zdWJtaXNzaW9uID0gdGliYmxlOjpyb3dpZF90b19jb2x1bW4obG1fc3VibWlzc2lvbiwnSWQnKQpyb3duYW1lcyhsbV9zdWJtaXNzaW9uKSA9IDE0NjE6KG5yb3cobG1fc3VibWlzc2lvbikrMTQ2MCkKbG1fc3VibWlzc2lvbiA9IG11dGF0ZShsbV9zdWJtaXNzaW9uLCBJZCA9IElkICsgMTQ2MCkKI3dyaXRlLmNzdihnYW0udGVzdF9wcmVkaWN0aW9ucywnLi9kYXRhL3ByaWNlX3ByZWRpY3Rpb25zLmNzdicpCmBgYAoKYGBge3J9CiMgd3JpdGUudGFibGUoZ21fc3VibWlzc2lvbixmaWxlPSIuL2RhdGEvZ21fcHJlZGljdGlvbnMuY3N2Iixjb2wubmFtZXMgPSBjKCJJZCIsIlNhbGVQcmljZSIpLHNlcCA9ICIsIixyb3cubmFtZXMgPSBGKQp3cml0ZS50YWJsZShsbV9zdWJtaXNzaW9uLGZpbGU9Ii4vZGF0YS9sbV9wcmVkaWN0aW9ucy5jc3YiLGNvbC5uYW1lcyA9IGMoIklkIiwiU2FsZVByaWNlIiksc2VwID0gIiwiLHJvdy5uYW1lcyA9IEYpCndyaXRlLnRhYmxlKGdtYl9zdWJtaXNzaW9uLGZpbGU9Ii4vZGF0YS9nbWJfcHJlZGljdGlvbnMuY3N2Iixjb2wubmFtZXMgPSBjKCJJZCIsIlNhbGVQcmljZSIpLHNlcCA9ICIsIixyb3cubmFtZXMgPSBGKQoKd3JpdGUudGFibGUoKGxtX3N1Ym1pc3Npb24rZ21iX3N1Ym1pc3Npb24pLzIsZmlsZT0iLi9kYXRhL2F2Z19wcmVkaWN0aW9ucy5jc3YiLGNvbC5uYW1lcyA9IGMoIklkIiwiU2FsZVByaWNlIiksc2VwID0gIiwiLHJvdy5uYW1lcyA9IEYpCmBgYAoKYGBge3J9CmRpbV9wcmVkID0gcmVhZF9jc3YoJy4uL0RtaXRyaS9wcmVkaWN0aW9uc18xNTkwNDM1NDQyLmNzdicpCmBgYAoKCmBgYHtyfQp0ZXN0LnByZWRzID0gdGVzdCAlPiUgbXV0YXRlKHByZWRzID0gcHJlZGljdC5nYW0oYW1lcy5nYW0uYmFzZSwgbmV3ZGF0YSA9IHRlc3QsIHR5cGUgPSAncmVzcG9uc2UnKSwgbG9ncHJlZHMgPSBsb2cocHJlZHMpKQoKdGVzdC5wcmVkcy5sbSA9IHRlc3QgJT4lIG11dGF0ZShwcmVkcyA9IHByZWRpY3QoYW1lcy5sbSwgbmV3ZGF0YSA9IHRlc3QsIHR5cGUgPSAncmVzcG9uc2UnKSwgbG9ncHJlZHMgPSBsb2cocHJlZHMpKQoKdGVzdC5wcmVkcy5kaW0gPSB0ZXN0ICU+JSBtdXRhdGUocHJlZHMgPSBkaW1fcHJlZCRTYWxlUHJpY2UsIGxvZ3ByZWRzID0gbG9nKHByZWRzKSkKCgpgYGAKCmBgYHtyfQoKdGVzdC5wcmVkcyRQcmljZVJhbmdlIDwtIGZhY3Rvcih0ZXN0LnByZWRzJFByaWNlUmFuZ2UsIGxldmVscyA9IGMoIkhpZ2giLCAiTWlkZGxlIiwgIkxvdyIpKQpwcmVkc191bnN0cnVjdCA9IAogIHRlc3QucHJlZHMgJT4lIAogIGdncGxvdChhZXMoeD0xOm5yb3codGVzdC5wcmVkcyksIHkgPSBwcmVkcywgY29sPVByaWNlUmFuZ2UpKSArIAogIGdlb21fcG9pbnQoKSArCiAgbGFicyh4ID0gJ1Rlc3QgU2V0IEluZGV4JywgeSA9ICdQcmljZSBQcmVkaWN0aW9ucycpICsKICB0aGVtZV9idygpCgpnZ3NhdmUocGFzdGUwKCIuL3ByZXNlbnRhdGlvbi9wcmVkc19nYW0vdW5zdHJ1Y3QucG5nIiksIHByZWRzX3Vuc3RydWN0KQoKcHJlZHNfc3RydWN0ID0gCiAgdGVzdC5wcmVkcyAlPiUgCiAgYXJyYW5nZShwcmVkcykgJT4lIAogIGdncGxvdChhZXMoeD0xOm5yb3codGVzdC5wcmVkcyksIHkgPSBwcmVkcywgY29sPVByaWNlUmFuZ2UpKSArIAogIGdlb21fcG9pbnQoKSArCiAgbGFicyh4ID0gJ1Rlc3QgU2V0IEluZGV4JywgeSA9ICdQcmljZSBQcmVkaWN0aW9ucycpICsKICB0aGVtZV9idygpCgoKZ2dzYXZlKHBhc3RlMCgiLi9wcmVzZW50YXRpb24vcHJlZHNfZ2FtL3N0cnVjdC5wbmciKSwgcHJlZHNfc3RydWN0KQoKCnByZWRzX3N0cnVjdF9sb2cgPSAKICB0ZXN0LnByZWRzICU+JSAKICBhcnJhbmdlKGxvZ3ByZWRzKSAlPiUgCiAgZ2dwbG90KGFlcyh4PTE6bnJvdyh0ZXN0LnByZWRzKSwgeSA9IGxvZ3ByZWRzLCBjb2w9UHJpY2VSYW5nZSkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBsYWJzKHggPSAnVGVzdCBTZXQgSW5kZXgnLCB5ID0gJ0xvZyBQcmljZSBQcmVkaWN0aW9ucycpICsKICB0aGVtZV9idygpCgoKZ2dzYXZlKHBhc3RlMCgiLi9wcmVzZW50YXRpb24vcHJlZHNfZ2FtL3N0cnVjdF9sb2cucG5nIiksIHByZWRzX3N0cnVjdF9sb2cpCgoKcHJlZHNfYmlubmVkID0gCiAgdGVzdC5wcmVkcyAlPiUgCiAgYXJyYW5nZShsb2dwcmVkcykgJT4lIAogIGdncGxvdChhZXMoeD0xOm5yb3codGVzdC5wcmVkcyksIHkgPSBsb2dwcmVkcywgY29sPVByaWNlUmFuZ2UpKSArIAogIGdlb21fcG9pbnQoKSArCiAgZmFjZXRfd3JhcCgnUHJpY2VSYW5nZScpICsKICBsYWJzKHggPSAnVGVzdCBTZXQgSW5kZXgnLCB5ID0gJ0xvZyBQcmljZSBQcmVkaWN0aW9ucycpICsKICB0aGVtZV9idygpCgoKZ2dzYXZlKHBhc3RlMCgiLi9wcmVzZW50YXRpb24vcHJlZHNfZ2FtL2Jpbm5lZC5wbmciKSwgcHJlZHNfYmlubmVkKQoKcXVhbnRzID0gCiAgZ2dwbG90KHRlc3QucHJlZHMsIGFlcyhzYW1wbGUgPSBsb2dwcmVkcywgY29sb3VyID0gUHJpY2VSYW5nZSkpICsKICBzdGF0X3FxKCkgKwogIHN0YXRfcXFfbGluZSgpCgoKZ2dzYXZlKHBhc3RlMCgiLi9wcmVzZW50YXRpb24vcHJlZHNfZ2FtL3F1YW50cy5wbmciKSwgcXVhbnRzKQpgYGAKCmBgYHtyfQp0ZXN0LnByZWRzLmxtJFByaWNlUmFuZ2UgPC0gZmFjdG9yKHRlc3QucHJlZHMubG0kUHJpY2VSYW5nZSwgbGV2ZWxzID0gYygiSGlnaCIsICJNaWRkbGUiLCAiTG93IikpCnByZWRzX3Vuc3RydWN0ID0gCiAgdGVzdC5wcmVkcy5sbSAlPiUgCiAgZ2dwbG90KGFlcyh4PTE6bnJvdyh0ZXN0LnByZWRzKSwgeSA9IHByZWRzLCBjb2w9UHJpY2VSYW5nZSkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBsYWJzKHggPSAnVGVzdCBTZXQgSW5kZXgnLCB5ID0gJ1ByaWNlIFByZWRpY3Rpb25zJykgKwogIHRoZW1lX2J3KCkKCmdnc2F2ZShwYXN0ZTAoIi4vcHJlc2VudGF0aW9uL3ByZWRzX2xtL3Vuc3RydWN0LnBuZyIpLCBwcmVkc191bnN0cnVjdCkKCnByZWRzX3N0cnVjdCA9IAogIHRlc3QucHJlZHMubG0gJT4lIAogIGFycmFuZ2UocHJlZHMpICU+JSAKICBnZ3Bsb3QoYWVzKHg9MTpucm93KHRlc3QucHJlZHMpLCB5ID0gcHJlZHMsIGNvbD1QcmljZVJhbmdlKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIGxhYnMoeCA9ICdUZXN0IFNldCBJbmRleCcsIHkgPSAnUHJpY2UgUHJlZGljdGlvbnMnKSArCiAgdGhlbWVfYncoKQoKCmdnc2F2ZShwYXN0ZTAoIi4vcHJlc2VudGF0aW9uL3ByZWRzX2xtL3RydWN0LnBuZyIpLCBwcmVkc19zdHJ1Y3QpCgoKcHJlZHNfc3RydWN0X2xvZyA9IAogIHRlc3QucHJlZHMubG0gJT4lIAogIGFycmFuZ2UobG9ncHJlZHMpICU+JSAKICBnZ3Bsb3QoYWVzKHg9MTpucm93KHRlc3QucHJlZHMpLCB5ID0gbG9ncHJlZHMsIGNvbD1QcmljZVJhbmdlKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIGxhYnMoeCA9ICdUZXN0IFNldCBJbmRleCcsIHkgPSAnTG9nIFByaWNlIFByZWRpY3Rpb25zJykgKwogIHRoZW1lX2J3KCkKCgpnZ3NhdmUocGFzdGUwKCIuL3ByZXNlbnRhdGlvbi9wcmVkc19sbS9zdHJ1Y3RfbG9nLnBuZyIpLCBwcmVkc19zdHJ1Y3RfbG9nKQoKCnByZWRzX2Jpbm5lZCA9IAogIHRlc3QucHJlZHMubG0gJT4lIAogIGFycmFuZ2UobG9ncHJlZHMpICU+JSAKICBnZ3Bsb3QoYWVzKHg9MTpucm93KHRlc3QucHJlZHMpLCB5ID0gbG9ncHJlZHMsIGNvbD1QcmljZVJhbmdlKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIGZhY2V0X3dyYXAoJ1ByaWNlUmFuZ2UnKSArCiAgbGFicyh4ID0gJ1Rlc3QgU2V0IEluZGV4JywgeSA9ICdMb2cgUHJpY2UgUHJlZGljdGlvbnMnKSArCiAgdGhlbWVfYncoKQoKCmdnc2F2ZShwYXN0ZTAoIi4vcHJlc2VudGF0aW9uL3ByZWRzX2xtL2Jpbm5lZC5wbmciKSwgcHJlZHNfYmlubmVkKQoKcXVhbnRzID0gCiAgZ2dwbG90KHRlc3QucHJlZHMubG0sIGFlcyhzYW1wbGUgPSBsb2dwcmVkcywgY29sb3VyID0gUHJpY2VSYW5nZSkpICsKICBzdGF0X3FxKCkgKwogIHN0YXRfcXFfbGluZSgpCgoKZ2dzYXZlKHBhc3RlMCgiLi9wcmVzZW50YXRpb24vcHJlZHNfbG0vcXVhbnRzLnBuZyIpLCBxdWFudHMpCmBgYAoKYGBge3J9CnRlc3QucHJlZHMuZGltJFByaWNlUmFuZ2UgPC0gZmFjdG9yKHRlc3QucHJlZHMuZGltJFByaWNlUmFuZ2UsIGxldmVscyA9IGMoIkhpZ2giLCAiTWlkZGxlIiwgIkxvdyIpKQpwcmVkc191bnN0cnVjdCA9IAogIHRlc3QucHJlZHMuZGltICU+JSAKICBnZ3Bsb3QoYWVzKHg9MTpucm93KHRlc3QucHJlZHMpLCB5ID0gcHJlZHMsIGNvbD1QcmljZVJhbmdlKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIGxhYnMoeCA9ICdUZXN0IFNldCBJbmRleCcsIHkgPSAnUHJpY2UgUHJlZGljdGlvbnMnKSArCiAgdGhlbWVfYncoKQoKZ2dzYXZlKHBhc3RlMCgiLi9wcmVzZW50YXRpb24vcHJlZHNfZGltL3Vuc3RydWN0LnBuZyIpLCBwcmVkc191bnN0cnVjdCkKCnByZWRzX3N0cnVjdCA9IAogIHRlc3QucHJlZHMuZGltICU+JSAKICBhcnJhbmdlKHByZWRzKSAlPiUgCiAgZ2dwbG90KGFlcyh4PTE6bnJvdyh0ZXN0LnByZWRzKSwgeSA9IHByZWRzLCBjb2w9UHJpY2VSYW5nZSkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBsYWJzKHggPSAnVGVzdCBTZXQgSW5kZXgnLCB5ID0gJ1ByaWNlIFByZWRpY3Rpb25zJykgKwogIHRoZW1lX2J3KCkKCgpnZ3NhdmUocGFzdGUwKCIuL3ByZXNlbnRhdGlvbi9wcmVkc19kaW0vdHJ1Y3QucG5nIiksIHByZWRzX3N0cnVjdCkKCgpwcmVkc19zdHJ1Y3RfbG9nID0gCiAgdGVzdC5wcmVkcy5kaW0gJT4lIAogIGFycmFuZ2UobG9nKHByZWRzKSkgJT4lIAogIGdncGxvdChhZXMoeD0xOm5yb3codGVzdC5wcmVkcyksIHkgPSBsb2dwcmVkcywgY29sPVByaWNlUmFuZ2UpKSArIAogIGdlb21fcG9pbnQoKSArCiAgbGFicyh4ID0gJ1Rlc3QgU2V0IEluZGV4JywgeSA9ICdMb2cgUHJpY2UgUHJlZGljdGlvbnMnKSArCiAgdGhlbWVfYncoKQoKCmdnc2F2ZShwYXN0ZTAoIi4vcHJlc2VudGF0aW9uL3ByZWRzX2RpbS9zdHJ1Y3RfbG9nLnBuZyIpLCBwcmVkc19zdHJ1Y3RfbG9nKQoKCnByZWRzX2Jpbm5lZCA9IAogIHRlc3QucHJlZHMuZGltICU+JSAKICBhcnJhbmdlKGxvZyhwcmVkcykpICU+JSAKICBnZ3Bsb3QoYWVzKHg9MTpucm93KHRlc3QucHJlZHMpLCB5ID0gbG9ncHJlZHMsIGNvbD1QcmljZVJhbmdlKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIGZhY2V0X3dyYXAoJ1ByaWNlUmFuZ2UnKSArCiAgbGFicyh4ID0gJ1Rlc3QgU2V0IEluZGV4JywgeSA9ICdMb2cgUHJpY2UgUHJlZGljdGlvbnMnKSArCiAgdGhlbWVfYncoKQoKCmdnc2F2ZShwYXN0ZTAoIi4vcHJlc2VudGF0aW9uL3ByZWRzX2RpbS9iaW5uZWQucG5nIiksIHByZWRzX2Jpbm5lZCkKCnF1YW50cyA9IAogIGdncGxvdCh0ZXN0LnByZWRzLmRpbSwgYWVzKHNhbXBsZSA9IGxvZ3ByZWRzLCBjb2xvdXIgPSBQcmljZVJhbmdlKSkgKwogIHN0YXRfcXEoKSArCiAgbGFicyh5ID0gJ0xvZyBQcmljZScpICsKICBzdGF0X3FxX2xpbmUoKQoKCmdnc2F2ZShwYXN0ZTAoIi4vcHJlc2VudGF0aW9uL3ByZWRzX2RpbS9xdWFudHMucG5nIiksIHF1YW50cykKYGBgCgoKYGBge3J9CgpzdGF0X2JveF9kYXRhIDwtIGZ1bmN0aW9uKHksIHVwcGVyX2xpbWl0ID0gbWF4KGFtZXMkU2FsZVByaWNlKSAqIDEuMTUpIHsKICByZXR1cm4oIAogICAgZGF0YS5mcmFtZSgKICAgICAgeSA9IDAuOTggKiB1cHBlcl9saW1pdCwKICAgICAgbGFiZWwgPSBwYXN0ZSgnY1xuJywgbGVuZ3RoKHkpLCAnXG4nKQogICAgKQogICkKfQoKCmcgPSBhbWVzICU+JSAKICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKE5laWdoYm9yaG9vZCxTYWxlUHJpY2UsIG1lYW4pLCB5ID0gU2FsZVByaWNlKSkgKyAKICBnZW9tX2JveHBsb3QoKSArCiAgc3RhdF9zdW1tYXJ5KAogICAgZnVuLmRhdGEgPSBzdGF0X2JveF9kYXRhLCAKICAgIGdlb20gPSAidGV4dCIsIAogICAgaGp1c3QgPSAwLjUsCiAgICB2anVzdCA9IDAuOQogICkgKyAKICB0aGVtZV9idygpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSArCiAgbGFicyh4PSdOZWlnaGJvcmhvb2QnLCB5ID0gJ1NhbGVQcmljZScpCgoKZ2dzYXZlKHBhc3RlMCgiLi9MaW5lYXIgTW9kZWwvaG9vZF9wcmljZS5wbmciKSwgZykKCmBgYAoKCmBgYHtyfQoKbWluX24gPSAyMAojIGFtZXNfdHJhaW4gJT4lCiMgICBncm91cF9ieShOZWlnaGJvcmhvb2QsIE92ZXJhbGxDb25kKSAlPiUKIyAgIHRhbGx5KCkgJT4lCiMgICBmaWx0ZXIobiA+IG1pbl9uKSAlPiUKIyAgIGdncGxvdChhZXMoeD1OZWlnaGJvcmhvb2QsIHk9T3ZlcmFsbENvbmQpKSArCiMgICBnZW9tX3BvaW50KCkgKwojICAgZ2VvbV90ZXh0KGNvbG9yPSdyZWQnLHNpemU9NCxhZXMoeT1PdmVyYWxsQ29uZCswLjIsIGxhYmVsPW4pKSArCiMgICB0aGVtZV9idygpICsKIyAgIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpCiMKIyBhbWVzX3RyYWluICU+JQojICAgZ3JvdXBfYnkoTVNTdWJDbGFzcywgT3ZlcmFsbFF1YWwpICU+JQojICAgdGFsbHkoKSAlPiUKIyAgIGZpbHRlcihuID4gbWluX24pICU+JQojICAgZ2dwbG90KGFlcyh4PWZhY3RvcihNU1N1YkNsYXNzKSwgeT1PdmVyYWxsUXVhbCkpICsKIyAgIGdlb21fcG9pbnQoKSArCiMgICBnZW9tX3RleHQoY29sb3I9J3JlZCcsc2l6ZT00LGFlcyh5PU92ZXJhbGxRdWFsKzAuMiwgbGFiZWw9bikpICsKIyAgIHRoZW1lX2J3KCkgKwojICAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkKIwojCiMgYW1lc190cmFpbiAlPiUKIyAgIGdyb3VwX2J5KEhvdXNlU3R5bGUsIE92ZXJhbGxRdWFsKSAlPiUKIyAgIHRhbGx5KCkgJT4lCiMgICBmaWx0ZXIobiA+IG1pbl9uKSAlPiUKIyAgIGdncGxvdChhZXMoeD1mYWN0b3IoSG91c2VTdHlsZSksIHk9T3ZlcmFsbFF1YWwpKSArCiMgICBnZW9tX3BvaW50KCkgKwojICAgZ2VvbV90ZXh0KGNvbG9yPSdyZWQnLHNpemU9NCxhZXMoeT1PdmVyYWxsUXVhbCswLjIsIGxhYmVsPW4pKSArCiMgICB0aGVtZV9idygpICsKIyAgIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpCgojIFpvbmluZyByZWxhdGlvbnNoaXAgd3J0IE92ZXJhbGwgUXVhbGl0eSBvZiB0aGUgSG9tZQojIEZsb2F0aW5nIFZpbGxhZ2UgUmVzaWRlbnRpYWwgaGFzIHNjb3JlID49IDYKIyBDb21tZXJjaWFsIHpvbmVzIGhhdmUgc2NvcmVzIDw9IDYKIyBMb3cgZGVuc2l0eSByZXNpZGVudGlhbCBzcGFucyBhbGwgc2NvcmVzCiMgbWVkaXVtIGRlbnNpdHkgdGVuZHMgdG93YXJkcyBoaWdoZXIgc2NvcmVzCgojIGFtZXNfdHJhaW4gJT4lCiMgICBncm91cF9ieShNU1pvbmluZywgT3ZlcmFsbFF1YWwpICU+JQojICAgdGFsbHkoKSAlPiUKIyAgIGZpbHRlcihuID4gbWluX24pICU+JQojICAgZ2dwbG90KGFlcyh4PWZhY3RvcihNU1pvbmluZyksIHk9T3ZlcmFsbFF1YWwpKSArCiMgICBnZW9tX3BvaW50KCkgKwojICAgZ2VvbV90ZXh0KGNvbG9yPSdyZWQnLHNpemU9NCxhZXMoeT1PdmVyYWxsUXVhbCswLjIsIGxhYmVsPW4pKSArCiMgICB0aGVtZV9idygpICsKIyAgIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpCiMKIyBhbWVzX3RyYWluICU+JQojICAgZ3JvdXBfYnkoU2FsZVR5cGUsIE92ZXJhbGxRdWFsKSAlPiUKIyAgIHRhbGx5KCkgJT4lCiMgICBmaWx0ZXIobiA+IG1pbl9uKSAlPiUKIyAgIGdncGxvdChhZXMoeD1mYWN0b3IoU2FsZVR5cGUpLCB5PU92ZXJhbGxRdWFsKSkgKwojICAgZ2VvbV9wb2ludCgpICsKIyAgIGdlb21fdGV4dChjb2xvcj0ncmVkJyxzaXplPTQsYWVzKHk9T3ZlcmFsbFF1YWwrMC4yLCBsYWJlbD1uKSkgKwojCiMgICB0aGVtZV9idygpICsKIyAgIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpCiMKIyBhbWVzICU+JQojICAgZ3JvdXBfYnkoU2FsZVR5cGUsIE92ZXJhbGxDb25kKSAlPiUKIyAgIHRhbGx5KCkgJT4lCiMgICBmaWx0ZXIobiA+IG1pbl9uKSAlPiUKIyAgIGdncGxvdChhZXMoeD1mYWN0b3IoU2FsZVR5cGUpLCB5PU92ZXJhbGxDb25kKSkgKwojICAgZ2VvbV9wb2ludCgpICsKIyAgIGdlb21fdGV4dChjb2xvcj0ncmVkJyxzaXplPTQsYWVzKHk9T3ZlcmFsbENvbmQrMC4yLCBsYWJlbD1uKSkgKwojICAgdGhlbWVfYncoKSArCiMgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQojIAojIGFtZXMgJT4lCiMgICBncm91cF9ieShHYXJhZ2VUeXBlLCBHYXJhZ2VDb25kKSAlPiUKIyAgIHRhbGx5KCkgJT4lCiMgICBmaWx0ZXIobiA+IG1pbl9uKSAlPiUKIyAgIGdncGxvdChhZXMoeD1mYWN0b3IoR2FyYWdlVHlwZSksIHk9R2FyYWdlQ29uZCkpICsKIyAgIGdlb21fcG9pbnQoKSArCiMgICBnZW9tX3RleHQoY29sb3I9J3JlZCcsc2l6ZT00LGFlcyh5PUdhcmFnZUNvbmQrMC4yLCBsYWJlbD1uKSkgKwojICAgdGhlbWVfYncoKSArCiMgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQoKblFfdGVzdCA9IHRlc3QgJT4lCiAgZ3JvdXBfYnkoTmVpZ2hib3Job29kLCBPdmVyYWxsUXVhbCkgJT4lCiAgdGFsbHkoKSAlPiUKICBmaWx0ZXIobiA+IG1pbl9uKSAlPiUKICBnZ3Bsb3QoYWVzKHg9cmVvcmRlcihOZWlnaGJvcmhvb2QsT3ZlcmFsbFF1YWwsbWF4KSwgeT1PdmVyYWxsUXVhbCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fdGV4dChjb2xvcj0ncmVkJyxzaXplPTQsYWVzKHk9T3ZlcmFsbFF1YWwrMC4yLCBsYWJlbD1uKSkgKwogIHRoZW1lX2J3KCkgKwogIGxhYnMoeCA9ICdOZWlnaGJvcmhvb2QgKFRlc3QgU2V0KScpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQoKCmdnc2F2ZShwYXN0ZTAoIi4vTGluZWFyIE1vZGVsL25RX3Rlc3QucG5nIiksIG5RX3Rlc3QpCgpuQ190ZXN0ID0gdGVzdCAlPiUKICBncm91cF9ieShOZWlnaGJvcmhvb2QsIE92ZXJhbGxDb25kKSAlPiUKICB0YWxseSgpICU+JQogIGZpbHRlcihuID4gbWluX24pICU+JQogIGdncGxvdChhZXMoeD1yZW9yZGVyKE5laWdoYm9yaG9vZCxPdmVyYWxsQ29uZCxtYXgpLCB5PU92ZXJhbGxDb25kKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV90ZXh0KGNvbG9yPSdyZWQnLHNpemU9NCxhZXMoeT1PdmVyYWxsQ29uZCswLjIsIGxhYmVsPW4pKSArCiAgdGhlbWVfYncoKSArCiAgbGFicyh4ID0gJ05laWdoYm9yaG9vZCAoVGVzdCBTZXQpJykgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpCgpnZ3NhdmUocGFzdGUwKCIuL0xpbmVhciBNb2RlbC9uQ190ZXN0LnBuZyIpLCBuQ190ZXN0KQoKblEgPSBhbWVzICU+JQogIGdyb3VwX2J5KE5laWdoYm9yaG9vZCwgT3ZlcmFsbFF1YWwpICU+JQogIHRhbGx5KCkgJT4lCiAgZmlsdGVyKG4gPiBtaW5fbikgJT4lCiAgZ2dwbG90KGFlcyh4PXJlb3JkZXIoTmVpZ2hib3Job29kLE92ZXJhbGxRdWFsLG1heCksIHk9T3ZlcmFsbFF1YWwpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3RleHQoY29sb3I9J3JlZCcsc2l6ZT00LGFlcyh5PU92ZXJhbGxRdWFsKzAuMiwgbGFiZWw9bikpICsKICB0aGVtZV9idygpICsKICBsYWJzKHggPSAnTmVpZ2hib3Job29kIChUcmFpbmluZyBTZXQpJykgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpCgoKZ2dzYXZlKHBhc3RlMCgiLi9MaW5lYXIgTW9kZWwvblEucG5nIiksIG5RKQoKbkMgPSBhbWVzICU+JQogIGdyb3VwX2J5KE5laWdoYm9yaG9vZCwgT3ZlcmFsbENvbmQpICU+JQogIHRhbGx5KCkgJT4lCiAgZmlsdGVyKG4gPiBtaW5fbikgJT4lCiAgZ2dwbG90KGFlcyh4PXJlb3JkZXIoTmVpZ2hib3Job29kLE92ZXJhbGxDb25kLG1heCksIHk9T3ZlcmFsbENvbmQpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3RleHQoY29sb3I9J3JlZCcsc2l6ZT00LGFlcyh5PU92ZXJhbGxDb25kKzAuMiwgbGFiZWw9bikpICsKICB0aGVtZV9idygpICsKICBsYWJzKHggPSAnTmVpZ2hib3Job29kIChUcmFpbmluZyBTZXQpJykgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpCgpnZ3NhdmUocGFzdGUwKCIuL0xpbmVhciBNb2RlbC9uQy5wbmciKSwgbkMpCgphbWVzICU+JQogIGdyb3VwX2J5KE92ZXJhbGxDb25kLCBPdmVyYWxsUXVhbCkgJT4lCiAgdGFsbHkoKSAlPiUKICBmaWx0ZXIobiA+IG1pbl9uKSAlPiUKICBnZ3Bsb3QoYWVzKHg9T3ZlcmFsbENvbmQsIHk9T3ZlcmFsbFF1YWwpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3RleHQoY29sb3I9J3JlZCcsc2l6ZT00LGFlcyh5PU92ZXJhbGxRdWFsKzAuMiwgbGFiZWw9bikpICsKICB0aGVtZV9idygpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQoKYW1lcyAlPiUKICBncm91cF9ieShPdmVyYWxsUXVhbCwgUHJpY2VSYW5nZSkgJT4lCiAgdGFsbHkoKSAlPiUKICBmaWx0ZXIobiA+IG1pbl9uKSAlPiUKICBnZ3Bsb3QoYWVzKHk9T3ZlcmFsbFF1YWwsIHggPSBQcmljZVJhbmdlKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV90ZXh0KGNvbG9yPSdyZWQnLHNpemU9NCxhZXMoeT1PdmVyYWxsUXVhbCswLjUsIGxhYmVsPW4pKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkKCmFtZXMgJT4lCiAgZ3JvdXBfYnkoT3ZlcmFsbENvbmQsIFByaWNlUmFuZ2UpICU+JQogIHRhbGx5KCkgJT4lCiAgZmlsdGVyKG4gPiBtaW5fbikgJT4lCiAgZ2dwbG90KGFlcyh5PU92ZXJhbGxDb25kLCB4ID0gUHJpY2VSYW5nZSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fdGV4dChjb2xvcj0ncmVkJyxzaXplPTQsYWVzKHk9T3ZlcmFsbENvbmQrMC41LCBsYWJlbD1uKSkgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpCgojYW1lcyRQcmljZVJhbmdlID1hbWVzJFNhbGVQcmljZSAlPiUgY3V0X251bWJlcig1LCBsYWJlbHM9YygnQ2hlYXAnLCdMb3dlciBNaWRkbGUnLCAnTWlkZGxlJywnVXBwZXIgTWlkZGxlJywnRXhwZW5zaXZlJykpCgoKCiNhbWVzICU+JSBtdXRhdGUoUHJpY2VSYW5nZSA9IGlmZWxzZShOZWlnaGJvcmhvb2QgJWluJSBtZWRpYW5zJE5laWdoYm9yaG9vZCwgZmFjdG9yKG1lZGlhbnMkUHJpY2VSYW5nZSksMCkpICU+JSBzZWxlY3QoYyhOZWlnaGJvcmhvb2QsUHJpY2VSYW5nZSkpCiNhbWVzID0gYW1lcyAlPiUgbGVmdF9qb2luKC4sIG1lZGlhbnNbLGMoJ05laWdoYm9yaG9vZCcsJ1ByaWNlUmFuZ2UnKV0sIGJ5PSdOZWlnaGJvcmhvb2QnKQoKYW1lcyAlPiUgCiAgZ3JvdXBfYnkoTmVpZ2hib3Job29kLCBQcmljZVJhbmdlLCBTYWxlUHJpY2UpICU+JSAKICBzdW1tYXJpc2UobWVkID0gbWVkaWFuKFNhbGVQcmljZSksIG4gPSBuKCkpICU+JSAKICBmaWx0ZXIobiA+IG1pbl9uKSAlPiUgCiAgZ2dwbG90KGFlcyh4PXJlb3JkZXIoTmVpZ2hib3Job29kLG4sbWF4KSwgeSA9IFByaWNlUmFuZ2UpKSArIAogIGdlb21fcG9pbnQoY29sb3I9J3doaXRlJykgKwogIGdlb21fdGV4dChjb2xvcj0ncmVkJyxzaXplPTQsYWVzKHk9UHJpY2VSYW5nZSwgbGFiZWw9bikpICsKICB0aGVtZV9idygpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSAKCgoKYW1lcyAlPiUgCiAgZ2dwbG90KGFlcyh4PVByaWNlUmFuZ2UsIHkgPSBTYWxlUHJpY2UpKSArIAogIGdlb21fYm94cGxvdCgpICsKICB0aGVtZV9idygpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSAKCmBgYApgYGB7cn0KYW1lcyAlPiUKICBncm91cF9ieShOZWlnaGJvcmhvb2QsIEV4dGVyQ29uZCkgJT4lCiAgdGFsbHkoKSAlPiUKICBmaWx0ZXIobiA+IDIwKSAlPiUKICBnZ3Bsb3QoYWVzKHg9TmVpZ2hib3Job29kLCB5PUV4dGVyQ29uZCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fdGV4dChjb2xvcj0ncmVkJyxzaXplPTQsYWVzKHk9RXh0ZXJDb25kKzAuMiwgbGFiZWw9bikpICsKICB0aGVtZV9idygpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQoKdGVzdCAlPiUKICBncm91cF9ieShFeHRlclF1YWwsIEV4dGVyQ29uZCkgJT4lCiAgdGFsbHkoKSAlPiUKICBnZ3Bsb3QoYWVzKHg9RXh0ZXJRdWFsLCB5PUV4dGVyQ29uZCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fdGV4dChjb2xvcj0ncmVkJyxzaXplPTQsYWVzKHk9RXh0ZXJDb25kKzAuMiwgbGFiZWw9bikpICsKICB0aGVtZV9idygpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQoKdGVzdCAlPiUKICBncm91cF9ieShOZWlnaGJvcmhvb2QsIEV4dGVyUXVhbCkgJT4lCiAgdGFsbHkoKSAlPiUKICBmaWx0ZXIobiA+IDIwKSAlPiUKICBnZ3Bsb3QoYWVzKHg9cmVvcmRlcihOZWlnaGJvcmhvb2QsRXh0ZXJRdWFsLG1heCksIHk9RXh0ZXJRdWFsKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV90ZXh0KGNvbG9yPSdyZWQnLHNpemU9NCxhZXMoeT1FeHRlclF1YWwrMC4yLCBsYWJlbD1uKSkgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpCmBgYAoKYGBge3J9CmFtZXMgJT4lIHRyYW5zbXV0ZShQb3BOZWlnaCA9IGlmZWxzZShOZWlnaGJvcmhvb2QgJWluJSBjKCdOQW1lcycsJ0NvbGxnQ3InKSwxLDApKQpgYGAKCgpgYGB7cn0KCnN0YXRfYm94X2RhdGEgPC0gZnVuY3Rpb24oeSwgdXBwZXJfbGltaXQgPSBtYXgoYW1lcyRTYWxlUHJpY2UpICogMS4xNSkgewogIHJldHVybiggCiAgICBkYXRhLmZyYW1lKAogICAgICB5ID0gMC45OCAqIHVwcGVyX2xpbWl0LAogICAgICBsYWJlbCA9IHBhc3RlKCdjb3VudFxuJywgbGVuZ3RoKHkpLCAnXG4nKQogICAgKQogICkKfQoKYW1lcyAlPiUgCiAgZ2dwbG90KGFlcyh4PXJlb3JkZXIoU2FsZVR5cGUsU2FsZVByaWNlLG1lZGlhbiksIHkgPSBTYWxlUHJpY2UpKSArIAogIGdlb21fYm94cGxvdCgpICsKICBzdGF0X3N1bW1hcnkoCiAgICBmdW4uZGF0YSA9IHN0YXRfYm94X2RhdGEsIAogICAgZ2VvbSA9ICJ0ZXh0IiwgCiAgICBoanVzdCA9IDAuNSwKICAgIHZqdXN0ID0gMC45CiAgKSArIAogIHRoZW1lX2J3KCkgKwogIGxhYnMoeCA9ICdTYWxlIFR5cGUnLCB5ID0gJ1NhbGUgUHJpY2UnKQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpIAoKICAKYW1lcyAlPiUgCiAgZ2dwbG90KGFlcyh5ID0gU2FsZVByaWNlLCB4ID0gT3ZlcmFsbFF1YWwsIGNvbCA9IFNhbGVUeXBlKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHRoZW1lX2J3KCkKCmFtZXMgJT4lIAogIGdncGxvdChhZXMoeSA9IFNhbGVQcmljZSwgeCA9IE92ZXJhbGxDb25kLCBjb2wgPSBTYWxlVHlwZSkpICsgCiAgZ2VvbV9wb2ludCgpICsKICB0aGVtZV9idygpCgoKYW1lcyAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gTG90QXJlYSwgeSA9IE92ZXJhbGxRdWFsLCBjb2wgPSBQcmljZVJhbmdlKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHRoZW1lX2J3KCkKCmFtZXMgJT4lIAogIGdncGxvdChhZXMoeCA9IEdyTGl2QXJlYSwgeSA9IE92ZXJhbGxRdWFsLCBjb2wgPSBQcmljZVJhbmdlKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHRoZW1lX2J3KCkKCmFtZXMgJT4lIAogIGdncGxvdChhZXMoeCA9IFNlY0ZsclNGLCB5ID0gT3ZlcmFsbFF1YWwsIGNvbCA9IFByaWNlUmFuZ2UpKSArIAogIGdlb21fcG9pbnQoKSArCiAgdGhlbWVfYncoKQogIAogIApgYGAKYGBge3J9CgpgYGAKCgpgYGB7cn0KbW9kZWwubm9pbnRlcmFjdGlvbiA9IGxtKFNhbGVQcmljZSB+IEV4dGVyQ29uZCArIHNxcnQoR2FyYWdlQXJlYSksIGRhdGE9YW1lc190cmFpbl8pCm1vZGVsLmludGVyYWN0aW9uID0gbG0oU2FsZVByaWNlIH4gRXh0ZXJDb25kOnNxcnQoR2FyYWdlQXJlYSksIGRhdGE9YW1lc190cmFpbl8pCgphbm92YShtb2RlbC5ub2ludGVyYWN0aW9uLCBtb2RlbC5pbnRlcmFjdGlvbikKYGBgCgoKYGBge3J9CnRlc3RfaW50ZXJhY3Rpb24gPSBmdW5jdGlvbihmZWF0dXJlMSwgZmVhdHVyZTIpIHsKICAKICBtb2RlbC5ub2ludGVyYWN0aW9uID0gbG0oU2FsZVByaWNlIH4gR3JMaXZBcmVhICsgZmVhdHVyZTEgKyBmZWF0dXJlMiwgZGF0YT1hbWVzX3RyYWluKQogIG1vZGVsLmludGVyYWN0aW9uID0gbG0oU2FsZVByaWNlIH4gR3JMaXZBcmVhICsgZmVhdHVyZTE6ZmVhdHVyZTIsIGRhdGE9YW1lc190cmFpbikKICAjcHJpbnQoc3VtbWFyeShtb2RlbC5pbnRlcmFjdGlvbikpCiAgcHJpbnQoYW5vdmEobW9kZWwubm9pbnRlcmFjdGlvbiwgbW9kZWwuaW50ZXJhY3Rpb24pKQp9CmBgYApgYGB7cn0KI3Rlc3RfaW50ZXJhY3Rpb24oYW1lc190cmFpbiRPdmVyYWxsQ29uZCwgYW1lc190cmFpbiROZWlnaGJvcmhvb2QpCiN0ZXN0X2ludGVyYWN0aW9uKGFtZXNfdHJhaW4kT3ZlcmFsbFF1YWwsIGFtZXNfdHJhaW4kTmVpZ2hib3Job29kKQphID0gdGVzdF9pbnRlcmFjdGlvbihhbWVzX3RyYWluJE5laWdoYm9yaG9vZCwgYW1lc190cmFpbiRPdmVyYWxsQ29uZCkKCmBgYApgYGB7cn0KCnRlc3RfaW50ZXJhY3Rpb24oYW1lc190cmFpbiRFeHRlckNvbmQsIHNxcnQoYW1lc190cmFpbiRHYXJhZ2VBcmVhKSkKYGBgCgoKIyMjIE51bWVyaWNhbCBGZWF0dXJlcwpgYGB7cn0KbGlicmFyeShnZ2ZvcnRpZnkpCmBgYAoKYGBge3J9CnJlcXVpcmUoZ3JpZEV4dHJhKQpwbG90X251bWVyaWNhbF9iYW5kcyA9IGZ1bmN0aW9uKGRmLCB4X25hbWUsIHlfbmFtZSkgewogIHggPSBkZlsseF9uYW1lXVtbMV1dCiAgeSA9IGRmWyx5X25hbWVdW1sxXV0KICBtb2RlbCA9IGxtKHkgfiB4LCBkYXRhPWRmKQogIHByZWRpY3RlZCA9IHByZWRpY3QobW9kZWwpCiAgCiAgCiAgCiAgZyA9IGdncGxvdChkZiwgYWVzKHgseSkpICsKICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIGNvbG9yID0gImxpZ2h0Z3JleSIpICsKICAgIGdlb21fc2VnbWVudChhZXMoeGVuZCA9IHgsIHllbmQ9cHJlZGljdGVkKSwgYWxwaGE9MC4yKSArCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj1hYnMobW9kZWwkcmVzaWR1YWxzKSkpICsKICAgIHNjYWxlX2NvbG9yX2NvbnRpbnVvdXMobG93ID0gImJsYWNrIiwgaGlnaCA9ICJyZWQiKSArCiAgICBndWlkZXMoY29sb3I9RkFMU0UpICsKICAgIGdlb21fcG9pbnQoYWVzKHk9cHJlZGljdGVkKSxzaGFwZT0xKSArCiAgICBsYWJzKHggPSB4X25hbWUsIHkgPSB5X25hbWUpICsKICAgIHRoZW1lX2J3KCkKICAKICAjIENvbnN0cnVjdGlvbiBvZiBDb25maWRlbmNlIEludGVydmFscwogIAogIGNsb3dlciA9IHByZWRpY3QobW9kZWwsIGludGVydmFsPSdjb25maWRlbmNlJywgbGV2ZWw9MC45NSlbLDJdCiAgY3VwcGVyID0gcHJlZGljdChtb2RlbCwgaW50ZXJ2YWw9J2NvbmZpZGVuY2UnLCBsZXZlbD0wLjk1KVssM10KICAKICAKICBwbG93ZXIgPSBwcmVkaWN0KG1vZGVsLCBpbnRlcnZhbD0ncHJlZGljdGlvbicsIGxldmVsPTAuOTUpWywyXQogIHB1cHBlciA9IHByZWRpY3QobW9kZWwsIGludGVydmFsPSdwcmVkaWN0aW9uJywgbGV2ZWw9MC45NSlbLDNdCiAgCiAgZzIgPSBnICsgZ2VvbV9yaWJib24oYWVzKHltaW4gPSBjbG93ZXIsIHltYXggPSBjdXBwZXIpLCBhbHBoYT0wLjMsIGNvbCA9ICdyZWQnKSArCiAgICBnZW9tX3JpYmJvbihhZXMoeW1pbj1wbG93ZXIsIHltYXggPSBwdXBwZXIpLCBhbHBoYT0wLjIsIGNvbD0nYmx1ZScpCgogIG1vZGVsX25hbWUgPSBwYXN0ZTAoeV9uYW1lLCd+Jyx4X25hbWUpCiAgZGlyLmNyZWF0ZShwYXN0ZTAoIi4vTGluZWFyIE1vZGVsLyIsbW9kZWxfbmFtZSkpCiAgCiAgIyBTYXZlIFNjYXR0ZXIgUGxvdAogIGdnc2F2ZShwYXN0ZTAoIi4vTGluZWFyIE1vZGVsLyIsbW9kZWxfbmFtZSwiL3NjYXR0ZXJfcGxvdC5wZGYiKSwgZzIpCiAgCiAgIyBTYXZlIERpYWdub3N0aWMgUExvdHMKICBwZGYocGFzdGUwKCIuL0xpbmVhciBNb2RlbC8iLG1vZGVsX25hbWUsIi9yZXNpZHVhbF9wbG90cy5wZGYiKSkKICBwYXIobWZyb3c9YygyLDIpKQogIHBsb3QobW9kZWwsd2hpY2g9YygxLDIsNCw2KSkKICBkZXYub2ZmKCkKICAKICAjIFNhdmUgSW5mbHVlbmNlIFBsb3QKICBwZGYocGFzdGUwKCIuL0xpbmVhciBNb2RlbC8iLG1vZGVsX25hbWUsIi9pbmZsdWVuY2UucGRmIikpCiAgaW5mID0gaW5mbHVlbmNlUGxvdChtb2RlbCkKCiAgZGV2Lm9mZigpCiAgCiAgCiAgIyBwbG90KHgsIAogICMgICAgICBhYnMobW9kZWwkcmVzaWR1YWxzKSwgCiAgIyAgICAgIHhsYWIgPSB4X25hbWUsIAogICMgICAgICB5bGFiID0gJ1Jlc2lkdWFsIFZhbHVlcyBTcXVhcmVkJywgCiAgIyAgICAgIG1haW4gPSAnU3F1YXJlZCBSZXNpZHVhbHMgY29tcGFyZWQgdG8gTVNFJykKICAjIGFibGluZShoPXNxcnQobWVhbihtb2RlbCRyZXNpZHVhbHNeMikpLCBsdHkgPSAyLCBsd2Q9MywgY29sID0gJ3JlZCcpCiAgIyBzcyA9IHNtb290aC5zcGxpbmUoeCwgbW9kZWwkcmVzaWR1YWxzXjIsIGN2ID0gVFJVRSkKICAjIGxpbmVzKHNzLCBsd2Q9MiwgY29sPSdibHVlJykKICAjIGFibGluZShoPW1lYW4oc3MkeSksIGx3ZD0yLCBjb2w9J2JsYWNrJykKICAjIGxlZ2VuZCgidG9wbGVmdCIsIAogICMgICAgICBjKCJNU0UiLCAiU3BsaW5lIiwgIlNwbGluZSBNZWFuIiksCiAgIyAgICAgIGx0eSA9IGMoMiwgMSwgMSksIAogICMgICAgICBjb2wgPSBjKCJyZWQiLCAiYmx1ZSIsICJibGFjayIpKQp9CmBgYAoKCgpgYGB7cn0KcmVxdWlyZShncmlkRXh0cmEpCnBsb3RfY2F0X2JhbmRzID0gZnVuY3Rpb24oZGYsIHhfbmFtZSwgeV9uYW1lKSB7CiAgeCA9IGRmWyx4X25hbWVdW1sxXV0KICB5ID0gZGZbLHlfbmFtZV1bWzFdXQogIG1vZGVsID0gbG0oeSB+IHgsIGRhdGE9ZGYpCiAgcHJlZGljdGVkID0gcHJlZGljdChtb2RlbCkKICAKICAKICAKICBnID0gZ2dwbG90KGRmLCBhZXMoeCx5KSkgKwogICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAibGlnaHRncmV5IikgKwogICAgZ2VvbV9zZWdtZW50KGFlcyh4ZW5kID0geCwgeWVuZD1wcmVkaWN0ZWQpLCBhbHBoYT0wLjIpICsKICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPWFicyhtb2RlbCRyZXNpZHVhbHMpKSkgKwogICAgc2NhbGVfY29sb3JfY29udGludW91cyhsb3cgPSAiYmxhY2siLCBoaWdoID0gInJlZCIpICsKICAgIGd1aWRlcyhjb2xvcj1GQUxTRSkgKwogICAgZ2VvbV9wb2ludChhZXMoeT1wcmVkaWN0ZWQpLHNoYXBlPTEpICsKICAgIGxhYnMoeCA9IHhfbmFtZSwgeSA9IHlfbmFtZSkgKwogICAgdGhlbWVfYncoKQogIAogICMgQ29uc3RydWN0aW9uIG9mIENvbmZpZGVuY2UgSW50ZXJ2YWxzCiAgCiAgY2xvd2VyID0gcHJlZGljdChtb2RlbCwgaW50ZXJ2YWw9J2NvbmZpZGVuY2UnLCBsZXZlbD0wLjk1KVssMl0KICBjdXBwZXIgPSBwcmVkaWN0KG1vZGVsLCBpbnRlcnZhbD0nY29uZmlkZW5jZScsIGxldmVsPTAuOTUpWywzXQogIAogIAogIHBsb3dlciA9IHByZWRpY3QobW9kZWwsIGludGVydmFsPSdwcmVkaWN0aW9uJywgbGV2ZWw9MC45NSlbLDJdCiAgcHVwcGVyID0gcHJlZGljdChtb2RlbCwgaW50ZXJ2YWw9J3ByZWRpY3Rpb24nLCBsZXZlbD0wLjk1KVssM10KICAKICBnMiA9IGcgKyBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IGNsb3dlciwgeW1heCA9IGN1cHBlciksIGFscGhhPTAuMywgY29sID0gJ3JlZCcpICsKICAgIGdlb21fcmliYm9uKGFlcyh5bWluPXBsb3dlciwgeW1heCA9IHB1cHBlciksIGFscGhhPTAuMiwgY29sPSdibHVlJykKCiAgbW9kZWxfbmFtZSA9IHBhc3RlMCh5X25hbWUsJ34nLHhfbmFtZSkKICBkaXIuY3JlYXRlKHBhc3RlMCgiLi9MaW5lYXIgTW9kZWwvIixtb2RlbF9uYW1lKSkKICAKICAjIFNhdmUgU2NhdHRlciBQbG90CiAgZ2dzYXZlKHBhc3RlMCgiLi9MaW5lYXIgTW9kZWwvIixtb2RlbF9uYW1lLCIvc2NhdHRlcl9wbG90LnBkZiIpLCBnMikKICAKICAjIFNhdmUgRGlhZ25vc3RpYyBQTG90cwogIHBkZihwYXN0ZTAoIi4vTGluZWFyIE1vZGVsLyIsbW9kZWxfbmFtZSwiL3Jlc2lkdWFsX3Bsb3RzLnBkZiIpKQogIHBhcihtZnJvdz1jKDIsMikpCiAgcGxvdChtb2RlbCx3aGljaD1jKDEsMiw0LDYpKQogIGRldi5vZmYoKQogIAogICMgU2F2ZSBJbmZsdWVuY2UgUGxvdAogIHBkZihwYXN0ZTAoIi4vTGluZWFyIE1vZGVsLyIsbW9kZWxfbmFtZSwiL2luZmx1ZW5jZS5wZGYiKSkKICBpbmYgPSBpbmZsdWVuY2VQbG90KG1vZGVsKQoKICBkZXYub2ZmKCkKICAKICAKICAjIHBsb3QoeCwgCiAgIyAgICAgIGFicyhtb2RlbCRyZXNpZHVhbHMpLCAKICAjICAgICAgeGxhYiA9IHhfbmFtZSwgCiAgIyAgICAgIHlsYWIgPSAnUmVzaWR1YWwgVmFsdWVzIFNxdWFyZWQnLCAKICAjICAgICAgbWFpbiA9ICdTcXVhcmVkIFJlc2lkdWFscyBjb21wYXJlZCB0byBNU0UnKQogICMgYWJsaW5lKGg9c3FydChtZWFuKG1vZGVsJHJlc2lkdWFsc14yKSksIGx0eSA9IDIsIGx3ZD0zLCBjb2wgPSAncmVkJykKICAjIHNzID0gc21vb3RoLnNwbGluZSh4LCBtb2RlbCRyZXNpZHVhbHNeMiwgY3YgPSBUUlVFKQogICMgbGluZXMoc3MsIGx3ZD0yLCBjb2w9J2JsdWUnKQogICMgYWJsaW5lKGg9bWVhbihzcyR5KSwgbHdkPTIsIGNvbD0nYmxhY2snKQogICMgbGVnZW5kKCJ0b3BsZWZ0IiwgCiAgIyAgICAgIGMoIk1TRSIsICJTcGxpbmUiLCAiU3BsaW5lIE1lYW4iKSwKICAjICAgICAgbHR5ID0gYygyLCAxLCAxKSwgCiAgIyAgICAgIGNvbCA9IGMoInJlZCIsICJibHVlIiwgImJsYWNrIikpCn0KYGBgCgpgYGB7cn0KCnNmX2JpbnMgPSByYmluX21hbnVhbChhbWVzX3RyYWluLCBTYWxlUHJpY2UsIFRvdGFsU0YsIGMoMWUyLDFlMywxZTQsMWU1LDFlNikpCgpwbG90KHNmX2JpbnMpCmBgYAoKYGBge3J9CmFtZXNfdHJhaW4gJT4lIAogIG11dGF0ZShCZWRyb29tQWJ2R3IgPSBhcy5mYWN0b3IoQmVkcm9vbUFidkdyKSkgJT4lIAogIGdyb3VwX2J5KEJlZHJvb21BYnZHcikgJT4lIAogIGdncGxvdChhZXMoeCA9IEdyTGl2QXJlYSwgeSA9IFNhbGVQcmljZSkpICsgZ2VvbV9wb2ludCgpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBUUlVFLCBjb2xvciA9ICJyZWQiKSArCiAgZmFjZXRfd3JhcChCZWRyb29tQWJ2R3J+LikKICAKYGBgCmBgYHtyfQpmZWF0dXJlID0gJ0FsbGV5JwphbWVzX3RyYWluICU+JSAKICBtdXRhdGUoZmVhdHVyZSA9IGFzLmZhY3RvcihmZWF0dXJlKSkgJT4lIAogIGdyb3VwX2J5KGZlYXR1cmUpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBUb3RhbFNGLCB5ID0gU2FsZVByaWNlKSkgKyBnZW9tX3BvaW50KCkgKyAKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IFRSVUUsIGNvbG9yID0gInJlZCIpICsKICAjc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPSdsb2cnKSArCiAgZmFjZXRfd3JhcChmZWF0dXJlKQoKZmVhdHVyZSA9ICdBbGxleScKYW1lc190cmFpbiAlPiUgCiAgI211dGF0ZShmZWF0dXJlID0gYXMuZmFjdG9yKGZlYXR1cmUpKSAlPiUgCiAgZ3JvdXBfYnkoQWxsZXkpICU+JSAKICBzdW1tYXJpc2UoU2FsZVByaWNlID0gbWVhbihTYWxlUHJpY2UpKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gQWxsZXksIHkgPSBTYWxlUHJpY2UpKSArIGdlb21fY29sKCkKICAjZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBUUlVFLCBjb2xvciA9ICJyZWQiKSArCiAgI3NjYWxlX3lfY29udGludW91cyh0cmFucz0nbG9nJykgKwogICNmYWNldF93cmFwKGZlYXR1cmUpCmBgYApgYGB7cn0KbGlicmFyeShzY2FsZXMpCmBgYAoKCgpgYGB7cn0KcGxvdF9wcmVkX2J5X25vbSA9IGZ1bmN0aW9uKG5vbV9mZWF0dXJlLCBwcmVkaWN0b3IsIHNjYWxlX2xvZyA9IEZBTFNFKSB7CiAgCiAgIyB4ID0gYW1lc190cmFpblsscHJlZGljdG9yXVtbMV1dCiAgIyAjeDIgPSBhbWVzX3RyYWluWyxub21fZmVhdHVyZV1bWzFdXQogICMgbW9kZWwgPSBsbShhbWVzX3RyYWluJFNhbGVQcmljZSB+IHgpCiAgIyBwcmVkaWN0ZWQgPSBwcmVkaWN0KG1vZGVsKQogIAogIGcgPSBhbWVzX3RyYWluICU+JSAKICAgIG11dGF0ZShub21fZmVhdHVyZSA9IGFzLmZhY3Rvcihub21fZmVhdHVyZSkpICU+JSAKICAgIGdyb3VwX2J5KG5vbV9mZWF0dXJlKSAlPiUgCiAgICBnZ3Bsb3QoYWVzX3N0cmluZyh4ID0gcHJlZGljdG9yLCB5ID0gJ1NhbGVQcmljZScpLCBlbnZpcm9ubWVudD1lbnZpcm9ubWVudCgpKSArIAogICAgZ2VvbV9wb2ludChzaGFwZT0xLGFscGhhPTAuNCkgKyAKICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgY29sb3IgPSAicmVkIiwgbGluZXR5cGU9J2Rhc2hlZCcpICsKICAgICMgZ2VvbV9zZWdtZW50KGFlcyh4ZW5kID0geCwgeWVuZD1wcmVkaWN0ZWQpLCBhbHBoYT0wLjIpICsKICAgICMgZ2VvbV9wb2ludChhZXMoY29sb3I9YWJzKG1vZGVsJHJlc2lkdWFscykpKSArCiAgICAjIHNjYWxlX2NvbG9yX2NvbnRpbnVvdXMobG93ID0gImJsYWNrIiwgaGlnaCA9ICJyZWQiKSArCiAgICAjIGd1aWRlcyhjb2xvcj1GQUxTRSkgKwogICAgIyBnZW9tX3BvaW50KGFlcyh5PXByZWRpY3RlZCksc2hhcGU9MSwgYWxwaGE9MC4yKSArCiAgICBmYWNldF93cmFwKG5vbV9mZWF0dXJlKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUwKHByZWRpY3RvciwnIHYgU2FsZSBQcmljZSBmb3IgZ2l2ZW4gJyxub21fZmVhdHVyZSkpICsKICAgIHRoZW1lX2J3KCkKICAKICAKICBtb2RlbF9uYW1lID0gcGFzdGUwKCdTYWxlUHJpY2V+JyxwcmVkaWN0b3IpCiAgaWYoc2NhbGVfbG9nKXsKICAgIGcgPSBnICsgCiAgICAgIHNjYWxlX3lfY29udGludW91cyh0cmFucz0nbG9nJykgKyAKICAgICAgbGFicyh0aXRsZT1wYXN0ZTAocHJlZGljdG9yLCcgdiBMb2cgU2FsZSBQcmljZSBmb3IgZ2l2ZW4gJyxub21fZmVhdHVyZSkpCiAgICAKICAgIG1vZGVsX25hbWUgPSBwYXN0ZTAoJ0xvZ19TYWxlUHJpY2V+JyxwcmVkaWN0b3IpCiAgfQogIAogIGZpbGVfbmFtZSA9IHBhc3RlMCgiLi9MaW5lYXIgTW9kZWwvT3JkaW5hbHMvIixub21fZmVhdHVyZSwnLycsbW9kZWxfbmFtZSwnLnBkZicpCiAgZGlyLmNyZWF0ZShwYXN0ZTAoIi4vTGluZWFyIE1vZGVsL09yZGluYWxzLyIsbm9tX2ZlYXR1cmUpKQogICMgU2F2ZSBTY2F0dGVyIFBsb3QKICBnZ3NhdmUoZmlsZV9uYW1lLCBnKQogICNkZXYub2ZmKCkKICBwcmludChnKQogIAp9CmBgYAoKYGBge3J9CnBsb3RfcHJlZF9ieV9ub20oJ01TU3ViQ2xhc3MnLCJUb3RhbFNGIiwgc2NhbGVfbG9nPUYpCmBgYAoKYGBge3J9CmZlYXR1cmUgPSAnSGFzUG9vbCcKYW1lc190cmFpbiAlPiUgCiAgbXV0YXRlKGZlYXR1cmUgPSBhcy5mYWN0b3IoZmVhdHVyZSkpICU+JSAKICAjZ3JvdXBfYnkoZmVhdHVyZSkgJT4lIAogIGdncGxvdChhZXMoeCA9IFRvdGFsU0YsIHkgPSBTYWxlUHJpY2UpKSArIGdlb21fcG9pbnQoKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgY29sb3IgPSAicmVkIikgKwogIGZhY2V0X2dyaWQoZmVhdHVyZSkgKwogIHNjYWxlX3lfY29udGludW91cyh0cmFucz0nbG9nJykKYGBgCgpgYGB7cn0KZmVhdHVyZSA9ICdIYXNHYXJhZ2UnCmFtZXNfdHJhaW4gJT4lIAogIG11dGF0ZShmZWF0dXJlID0gYXMuZmFjdG9yKGZlYXR1cmUpKSAlPiUgCiAgZ3JvdXBfYnkoZmVhdHVyZSkgJT4lIAogIGdncGxvdChhZXMoeCA9IFRvdGFsU0YsIHkgPSBTYWxlUHJpY2UpKSArIGdlb21fcG9pbnQoKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIGNvbG9yID0gInJlZCIpICsKICBmYWNldF93cmFwKGZlYXR1cmUpICsKICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9J2xvZycpCmBgYAoKYGBge3J9Cgpib3hwbG90KGxvZyhhbWVzX3RyYWluJFNhbGVQcmljZSkgfiBhbWVzX3RyYWluJE1vU29sZCwgb3V0bGluZSA9IEZBTFNFKQpib3hwbG90KGFtZXNfdHJhaW4kU2FsZVByaWNlIH4gYW1lc190cmFpbiRZclNvbGQsIG91dGxpbmUgPSBGQUxTRSkKYm94cGxvdChsb2coYW1lc190cmFpbiRTYWxlUHJpY2UpIH4gYW1lc190cmFpbiRZZWFyQnVpbHQsIG91dGxpbmUgPSBGQUxTRSkKYGBgCgoKCmBgYHtyfQpmZWF0dXJlID0gJ0JzbXRFeHBvc3VyZScKYW1lc190cmFpbiAlPiUgCiAgbXV0YXRlKGZlYXR1cmUgPSBhcy5mYWN0b3IoZmVhdHVyZSkpICU+JSAKICBncm91cF9ieShmZWF0dXJlKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gVG90YWxTRiwgeSA9IFNhbGVQcmljZSkpICsgZ2VvbV9wb2ludCgpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAicmVkIikgKwogIHNjYWxlX3lfY29udGludW91cyh0cmFucz0nbG9nJykgKwogIGZhY2V0X3dyYXAoZmVhdHVyZSkKYGBgCkxldHMgbG9vayBhdCBUb3RhbCBzcXVhcmUgZm9vdGFnZSBhY3Jvc3MgZGlmZmVyZW50IGRlZmluZWQgc3ViIGNsYXNzZXMKYGBge3J9CmZlYXR1cmUgPSAnTVNTdWJDbGFzcycKZyA9IGFtZXNfdHJhaW4gJT4lIAogICNmaWx0ZXIoZmVhdHVyZSA8IDcwLjApICU+JSAKICBtdXRhdGUoZmVhdHVyZSA9IGFzLmZhY3RvcihmZWF0dXJlKSkgJT4lIAogIGdyb3VwX2J5KGZlYXR1cmUpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBUb3RhbFNGLCB5ID0gU2FsZVByaWNlKSkgKyBnZW9tX3BvaW50KCkgKyAKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJyZWQiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPSdsb2cnKSArCiAgZmFjZXRfd3JhcChmZWF0dXJlKQoKCmdnc2F2ZSgiLi9MaW5lYXIgTW9kZWwvTXNTdWJDbGFzc19sb2dwbG90LnBkZiIsIGcpCmBgYAoKCgpgYGB7cn0KZmVhdHVyZSA9ICdNU1N1YkNsYXNzJwphbWVzX3RyYWluICU+JSAKICBmaWx0ZXIoQWZ0ZXJXVzI9PTEpICU+JSAKICBtdXRhdGUoZmVhdHVyZSA9IGFzLmZhY3RvcihmZWF0dXJlKSkgJT4lIAogIGdyb3VwX2J5KGZlYXR1cmUpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBHckxpdkFyZWEsIHkgPSBTYWxlUHJpY2UpKSArIGdlb21fcG9pbnQoKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgY29sb3IgPSAicmVkIikgKwogICNnZW9tX3NwbGluZShjb2xvciA9ICJibHVlIikgKwogICNzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9J2xvZycpICsKICBmYWNldF93cmFwKGZlYXR1cmUpCmBgYAoKCmBgYHtyfQoKZ2dwbG90KGFlcyh4LCBhYnMobW9kZWwkcmVzaWR1YWxzKSksIAogICAgIHhsYWIgPSB4X25hbWUsIAogICAgIHlsYWIgPSAnUmVzaWR1YWwgVmFsdWVzIFNxdWFyZWQnLCAKICAgICBtYWluID0gJ1NxdWFyZWQgUmVzaWR1YWxzIGNvbXBhcmVkIHRvIE1TRScpCmFibGluZShoPXNxcnQobWVhbihtb2RlbCRyZXNpZHVhbHNeMikpLCBsdHkgPSAyLCBsd2Q9MywgY29sID0gJ3JlZCcpCnNzID0gc21vb3RoLnNwbGluZSh4LCBtb2RlbCRyZXNpZHVhbHNeMiwgY3YgPSBUUlVFKQpsaW5lcyhzcywgbHdkPTIsIGNvbD0nYmx1ZScpCmFibGluZShoPW1lYW4oc3MkeSksIGx3ZD0yLCBjb2w9J2JsYWNrJykKYGBgCmBgYHtyfQptb2RlbCA8LSBldmFsKGJxdW90ZShsbSguKGYpLCBkYXRhID0gYW1lc190cmFpbikpKQpgYGAKCgpgYGB7cn0KbGlicmFyeShicm9vbSkKCiMgU3RlcHMgMSBhbmQgMgpkIDwtIGxtKG1wZyB+IGhwLCBkYXRhID0gbXRjYXJzKSAlPiUgCiAgICAgICBhdWdtZW50KCkKCmhlYWQoZCkKCiMgU3RlcHMgMyBhbmQgNApnZ3Bsb3QoZCwgYWVzKHggPSBocCwgeSA9IG1wZykpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJsaWdodGdyZXkiKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ZW5kID0gaHAsIHllbmQgPSAuZml0dGVkKSwgYWxwaGEgPSAuMikgKyAgIyBOb3RlIGAuZml0dGVkYAogIGdlb21fcG9pbnQoYWVzKGFscGhhID0gYWJzKC5yZXNpZCkpKSArICAjIE5vdGUgYC5yZXNpZGAKICBndWlkZXMoYWxwaGEgPSBGQUxTRSkgKwogIGdlb21fcG9pbnQoYWVzKHkgPSAuZml0dGVkKSwgc2hhcGUgPSAxKSArICAjIE5vdGUgYC5maXR0ZWRgCiAgdGhlbWVfYncoKQpgYGAKCgoKYGBge3J9CgojIFRoZSBuZXcgbGluZSBvZiBjb2RlCgoKbW9kZWwgPSBsbShsb2coU2FsZVByaWNlKSB+IGxvZyhUb3RhbFNGKSwgZGF0YT1hbWVzX3RyYWluKQpwcmVkaWN0ZWQgPSBwcmVkaWN0KG1vZGVsKQoKZyA9IGFtZXNfdHJhaW4gJT4lIAogIG11dGF0ZShTYWxlUHJpY2UgPSBsb2coU2FsZVByaWNlKSwgVG90YWxTRiA9IGxvZyhUb3RhbFNGKSkgJT4lIAogIGdncGxvdChhZXMoeD1Ub3RhbFNGLHk9U2FsZVByaWNlKSkgKwogICNnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIGNvbG9yID0gImxpZ2h0Z3JleSIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHhlbmQgPSBUb3RhbFNGLCB5ZW5kPXByZWRpY3RlZCksIGFscGhhPTAuMikgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yPWFicyhtb2RlbCRyZXNpZHVhbHMpKSkgKwogIHNjYWxlX2NvbG9yX2NvbnRpbnVvdXMobG93ID0gImJsYWNrIiwgaGlnaCA9ICJyZWQiKSArCiAgZ3VpZGVzKGNvbG9yPUZBTFNFKSArCiAgZ2VvbV9wb2ludChhZXMoeT1wcmVkaWN0ZWQpLHNoYXBlPTEpICsKICB0aGVtZV9idygpCgojIENvbnN0cnVjdGlvbiBvZiBDb25maWRlbmNlIEludGVydmFscwoKY2xvd2VyID0gcHJlZGljdChtb2RlbCwgaW50ZXJ2YWw9J2NvbmZpZGVuY2UnLCBsZXZlbD0wLjk1KVssMl0KY3VwcGVyID0gcHJlZGljdChtb2RlbCwgaW50ZXJ2YWw9J2NvbmZpZGVuY2UnLCBsZXZlbD0wLjk1KVssM10KCgpwbG93ZXIgPSBwcmVkaWN0KG1vZGVsLCBpbnRlcnZhbD0ncHJlZGljdGlvbicsIGxldmVsPTAuOTUpWywyXQpwdXBwZXIgPSBwcmVkaWN0KG1vZGVsLCBpbnRlcnZhbD0ncHJlZGljdGlvbicsIGxldmVsPTAuOTUpWywzXQoKZyArIGdlb21fcmliYm9uKGFlcyh5bWluID0gY2xvd2VyLCB5bWF4ID0gY3VwcGVyKSwgYWxwaGE9MC4zLCBjb2wgPSAncmVkJykgKwogIGdlb21fcmliYm9uKGFlcyh5bWluPXBsb3dlciwgeW1heCA9IHB1cHBlciksIGFscGhhPTAuMiwgY29sPSdibHVlJykKYGBgCkRlZmluaXRlbHkgYSBsb2dhcml0aGltaWMgcmVsYXRpb25zaGlwLi4uCgoKYGBge3J9CnFxbm9ybShtb2RlbCRyZXNpZHVhbHMpCnFxbGluZShtb2RlbCRyZXNpZHVhbHMpCmBgYAoKCmBgYHtyfQphbWVzX3RyYWluICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBUb3RhbFNGLCB5ID0gU2FsZVByaWNlKSkgKwogICNnZW9tX3BvaW50KCkgKwogIGdlb21faGV4KGJpbnM9NTUpCgpgYGAKCgojIyMgQXV0b21hdGluZyBWYXJpYWJsZSBTZWxlY3Rpb24gUHJvY2VzcwoKQWNjb3JkaW5nIHRvIEFrYWlrZSBDcml0ZXJpb24KYGBge3J9CmxpYnJhcnkoTUFTUykKYGBgCgpgYGB7cn0KbW9kZWwuZW1wdHkgPSBtZ2N2OjpnYW0obG9nKFNhbGVQcmljZSkgfiAxLCBkYXRhID0gYW1lcykgI1RoZSBtb2RlbCB3aXRoIGFuIGludGVyY2VwdCBPTkxZLgptb2RlbC5mdWxsID0gYW1lcy5nYW0gI1RoZSBtb2RlbCB3aXRoIEFMTCB2YXJpYWJsZXMuCnNjb3BlID0gbGlzdChsb3dlciA9IGZvcm11bGEobW9kZWwuZW1wdHkpLCB1cHBlciA9IGZvcm11bGEobW9kZWwuZnVsbCkpCmBgYAoKYGBge3J9CmZvcndhcmRBSUMgPSBzdGVwKG1vZGVsLmVtcHR5LCBzY29wZSwgZGlyZWN0aW9uID0gImZvcndhcmQiLCBrID0gMikKYmFja3dhcmRBSUMgPSBzdGVwKG1vZGVsLmZ1bGwsIHNjb3BlLCBkaXJlY3Rpb24gPSAiYmFja3dhcmQiLCBrID0gMikKYm90aEFJQy5lbXB0eSA9IHN0ZXAobW9kZWwuZW1wdHksIHNjb3BlLCBkaXJlY3Rpb24gPSAiYm90aCIsIGsgPSAyKQpib3RoQUlDLmZ1bGwgPSBzdGVwKG1vZGVsLmZ1bGwsIHNjb3BlLCBkaXJlY3Rpb24gPSAiYm90aCIsIGsgPSAyKQpgYGAKCmBgYHtyfQpzdW1tYXJ5KGJvdGhBSUMuZW1wdHkpCmBgYAoKYGBge3J9CmxpYnJhcnkobWdjdikKc2V0LnNlZWQoMCk7biA8LSA0MDAKZGF0IDwtIGdhbVNpbSgxLG49bixzY2FsZT0yKQphdHRhY2goZGF0KQojIyBOb3RlIHRoZSBpbmNyZWFzZWQgZ2FtbWEgcGFyYW1ldGVyIGJlbG93IHRvIGZhdm91cgojIyBzbGlnaHRseSBzbW9vdGhlciBtb2RlbHMuLi4KYjwtbWdjdjo6Z2FtKHl+IHMoeDAsYnM9InRzIikrCiAgICAgICAgICAgICAgIHMoeDEsYnM9InRzIikrCiAgICAgICAgICAgICAgIHMoeDIsYnM9InRzIikrCiAgICAgICAgICAgICAgIHMoeDMsYnM9InRzIiksCiAgICAgICAgICAgICBnYW1tYT0xLjQpCnN1bW1hcnkoYikKcGxvdChiLHBhZ2VzPTEpCgojIyBTYW1lIGFnYWluIHVzaW5nIFJFTUwvTUwKYjwtZ2FtKHl+cyh4MCxicz0idHMiKStzKHgxLGJzPSJ0cyIpK3MoeDIsYnM9InRzIikrCiAgIHMoeDMsYnM9InRzIikrcyh4NCxicz0idHMiKStzKHg1LGJzPSJ0cyIpLG1ldGhvZD0iUkVNTCIpCnN1bW1hcnkoYikKcGxvdChiLHBhZ2VzPTEpCgojIyBBbmQgb25jZSBtb3JlLCBidXQgdXNpbmcgdGhlIG51bGwgc3BhY2UgcGVuYWxpemF0aW9uCmI8LWdhbSh5fnMoeDAsYnM9ImNyIikrcyh4MSxicz0iY3IiKStzKHgyLGJzPSJjciIpKwogICBzKHgzLGJzPSJjciIpK3MoeDQsYnM9ImNyIikrcyh4NSxicz0iY3IiKSwKICAgbWV0aG9kPSJSRU1MIixzZWxlY3Q9VFJVRSkKc3VtbWFyeShiKQpwbG90KGIscGFnZXM9MSkKCgpkZXRhY2goZGF0KTtybShkYXQpCgpgYGAKCgpgYGB7cn0KaW5mbHVlbmNlUGxvdChib3RoQUlDLmZ1bGwpCmBgYAojIyMgUHJlZGljdGlvbiBGdW5jdGlvbgpgYGB7cn0KbWFrZV9wcmVkID0gZnVuY3Rpb24obW9kZWwsIGRhdGEpIHsKICBkYXRhID0gZGF0YVssYXR0cihtb2RlbCR0ZXJtcywgJ3Rlcm0ubGFiZWxzJyldCiAgcHJlZC5iYW5kID0gcHJlZGljdChtb2RlbCwgZGF0YSwgaW50ZXJ2YWw9J3ByZWRpY3Rpb24nKQp9CmBgYAoKCgpgYGB7cn0Kc2FwcGx5KG51bV90cmFpbltyb3duYW1lcyhpbmYpLGF0dHIoYm90aEFJQy5mdWxsJHRlcm1zLCAndGVybS5sYWJlbHMnKV0sZXhwKSAlPiUgZGF0YS5mcmFtZSgpCmBgYAoKYGBge3J9CnN1bW1hcnkoYm90aEFJQy5lbXB0eSkKYGBgCgoKCiMjIyBEaXNjcmV0ZSBOdW1lcmljYWwgRmVhdHVyZXMgRURBCgpgYGB7cn0KZmVhdHVyZXMgPSBjKCdZZWFyQnVpbHQnLCdZZWFyUmVtb2RBZGQnLCdCc210RnVsbEJhdGgnLCdCc210SGFsZkJhdGgnLCdGdWxsJywnU2FsZVByaWNlJykKCnRyYWluX2RpcyA9IHRyYWluWyxmZWF0dXJlc10KI3Rlc3RfZGlzID0gdGVzdFssZmVhdHVyZXNdCgpgYGAKCmBgYHtyfQpjbGVhbl9kYXRhX3N0ciA9IGZ1bmN0aW9uKGRhdGVfc3RyKSB7CiAgbWR5ID0gbWR5KGRhdGVfc3RyKQogIHltZCA9IHltZChkYXRlX3N0cikKICAKICBpZihpcy5uYShtZHkpKXsKICAgIGlmKGlzLm5hKHltZCkpewogICAgICByZXR1cm4oTkEpCiAgICB9CiAgICBlbHNlIHtyZXR1cm4oeW1kKX0KICB9CiAgZWxzZSB7cmV0dXJuKG1keSl9Cn0KCmBgYAoKYGBge3J9CnRyYWluX2RpcyAlPiUgZ2dwbG90KGFlcyh4ID0gWWVhckJ1aWx0LCB5ID0gU2FsZVByaWNlKSkgKyBnZW9tX3BvaW50KCkKYGBgCgpTcGxpdCBpbnRvIHRlc3QgYW5kIHRyYWluCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCnggPSBtb2RlbC5tYXRyaXgoU2FsZVByaWNlIH4gLiwgYW1lcylbLCAtMV0KeSA9IGFtZXMkU2FsZVByaWNlCgoKc2V0LnNlZWQoMCkKdHJhaW4gPSBzYW1wbGUoMTpucm93KHgpLCA3Km5yb3coeCkvMTApCnkudGVzdCA9IHlbLXRyYWluXQoKCmBgYAoKCgpgYGB7cn0KbGlicmFyeShjYXJldCkKc2V0LnNlZWQoMCkKZ3JpZCA9IDEwXnNlcSg1LCAtMiwgbGVuZ3RoID0gMTAwKQpgYGAKCmBgYHtyfQoKY3YubGFzc28ub3V0ID0gY3YuZ2xtbmV0KHhbdHJhaW4sXSx5W3RyYWluXSwgbGFtYmRhPWdyaWQsIGFscGhhPTEsIG5mb2xkcz0xMCkKcGxvdChjdi5sYXNzby5vdXQpCgpgYGAKCmBgYHtyfQpiZXN0bGFtYmRhLmxhc3NvID0gY3YubGFzc28ub3V0JGxhbWJkYS5taW4KYmVzdGxhbWJkYS5sYXNzbwpsb2coYmVzdGxhbWJkYS5sYXNzbykKYGBgCgpgYGB7cn0KbGFzc28uYmVzdGxhbWJkYXRyYWluID0gcHJlZGljdChsYXNzby5tb2RlbHMudHJhaW4sIHMgPSBiZXN0bGFtYmRhLmxhc3NvLCBuZXd4ID0geFstdHJhaW4sXSkKCm1lYW4oKGxhc3NvLmJlc3RsYW1iZGF0cmFpbiAtIHkudGVzdCleMikKYGBgCmBgYHtyfQp0bXBfY29lZmZzIDwtIGNvZWYoY3YuZ2xtbmV0LmZpdCwgcyA9ICJsYW1iZGEubWluIikKZGF0YS5mcmFtZShuYW1lID0gdG1wX2NvZWZmc0BEaW1uYW1lc1tbMV1dW3RtcF9jb2VmZnNAaSArIDFdLCBjb2VmZmljaWVudCA9IHRtcF9jb2VmZnNAeCkKYGBgCgpgYGB7cn0KY29lZnMubGFzc28gPSBjb2VmKGN2Lmxhc3NvLm91dCkKYGBgCgoKYGBge3J9CnN0cihjb2Vmcy5sYXNzbykKYGBgCgpgYGB7cn0KY29lZnMubGFzc29AeApgYGAKCmBgYHtyfQpnYW0xID0gbWdjdjo6Z2FtKFNhbGVQcmljZSB+IHMoVG90YWxTRiwgYnM9J3BzJywgc3A9MC42KSArIHMoS2l0Y2hlbkFidkdyLCBicz0ncHMnLCBzcD0wLjYpLCBkYXRhPWFtZXNfdHJhaW4pCmBgYAoKYGBge3J9CiMjIyBHQU0gZXhhbXBsZSB1c2luZyBtZ2N2CgpsaWJyYXJ5KG1nY3YpCmxpYnJhcnkoZ2dwbG90MikKIyBmYWtlIGRhdGEKbiA8LSA1MApzaWcgPC0gMgpkYXQgPC0gZ2FtU2ltKDEsbj1uLHNjYWxlPXNpZykKCiMgUC1zcGxpbmUgc21vb3RoZXJzICh3aXRoIGxhbWJkYT0wLjYpIHVzZWQgZm9yIHgxIGFuZCB4MjsgeDMgaXMgcGFyYW1ldHJpYy4KYjEgPC0gbWdjdjo6Z2FtKHkgfiBzKHgxLCBicz0ncHMnLCBzcD0wLjYpICsgcyh4MiwgYnM9J3BzJywgc3A9MC42KSArIHgzLCBkYXRhID0gZGF0KQpzdW1tYXJ5KGIxKQpwbG90KGIxKQoKCiMgcGxvdCB0aGUgc21vb3RoIHByZWRpY3RvciBmdW5jdGlvbiBmb3IgeDEgd2l0aCBnZ3Bsb3QgdG8gZ2V0IGEgbmljZXIgbG9va2luZyBncmFwaApwIDwtIHByZWRpY3QoYjEsIHR5cGU9ImxwbWF0cml4IikKYmV0YSA8LSBjb2VmKGIxKVtncmVwbCgieDEiLCBuYW1lcyhjb2VmKGIxKSkpXQpzIDwtIHBbLGdyZXBsKCJ4MSIsIGNvbG5hbWVzKHApKV0gJSolIGJldGEKZ2dwbG90KGRhdGE9Y2JpbmQuZGF0YS5mcmFtZShzLCBkYXQkeDEpLCBhZXMoeD1kYXQkeDEsIHk9cykpICsgZ2VvbV9saW5lKCkKCgojIHByZWRpY3QKbmV3ZGYgPC0gZ2FtU2ltKDEsbj1uLHNjYWxlPXNpZykKZiA8LSBwcmVkaWN0KGIxLCBuZXdkYXRhPW5ld2RmKQoKCiMgc2VsZWN0IHNtb290aGluZyBwYXJhbWV0ZXJzIHdpdGggUkVNTCwgdXNpbmcgUC1zcGxpbmVzCmIyIDwtIG1nY3Y6OmdhbSh5IH4gcyh4MSwgYnM9J3BzJykgKyBzKHgyLCBicz0ncHMnKSArIHgzLCBkYXRhID0gZGF0LCBtZXRob2Q9IlJFTUwiKQoKIyBzZWxlY3QgdmFyaWFibGVzIGFuZCBzbW9vdGhpbmcgcGFyYW1ldGVycwpiMyA8LSBtZ2N2OjpnYW0oeSB+IHMoeDApICsgcyh4MSkgKyBzKHgyKSArIHMoeDMpICwgZGF0YSA9IGRhdCwgbWV0aG9kPSJSRU1MIiwgc2VsZWN0PVRSVUUpCgojIGxvZXNzIHNtb290aGVycyB3aXRoIHRoZSBnYW0gcGFja2FnZSAocmVzdGFydCBSIGJlZm9yZSBsb2FkaW5nIGdhbSkKbGlicmFyeShnYW0pCmI0IDwtIGdhbTo6Z2FtKHkgfiBsbyh4MSwgc3Bhbj0wLjYpICsgbG8oeDIsIHNwYW49MC42KSArIHgzLCBkYXRhID0gZGF0KQpzdW1tYXJ5KGI0KQpgYGAKCmBgYHtyfQpmZWF0dXJlID0gJ092ZXJhbGxRdWFsJwoKZGYgPSBhbWVzX3RyYWluICU+JSBncm91cF9ieShBZnRlcldXMixIb3VzZVN0eWxlKSAlPiUgc3VtbWFyaXNlKHByaWNlX21lZCA9IG1lZGlhbihTYWxlUHJpY2UpLCBuID1uKCkpCgphbWVzX3RyYWluICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBmYWN0b3IoSG91c2VTdHlsZSksIHkgPSBTYWxlUHJpY2UpKSArIGdlb21fYm94cGxvdCgpICsKICBnZW9tX3RleHQoZGF0YT1kZiwgY29sb3I9J3JlZCcsc2l6ZT00LGFlcyh5PXByaWNlX21lZCowLjgsIGxhYmVsPW4pKSArCiAgZmFjZXRfZ3JpZCgufmZhY3RvcihBZnRlcldXMikpICsKICBsYWJzKCkgKwogICNzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9J2xvZycpICsKICB0aGVtZV9idygpCgpgYGAKCmBgYHtyfQptb2RlbC5maXJlID0gbG0obG9nKFNhbGVQcmljZSkgfiBzcXJ0KEdyTGl2QXJlYSkgKyBzcXJ0KFRvdGFsQnNtdFNGKSArIE92ZXJhbGxDb25kICsgQWZ0ZXJXVzIgKyBGaXJlcGxhY2VzICsgSXNOZXcgKyBPdmVyYWxsUXVhbCwgZGF0YSA9IGFtZXMpCnN1bW1hcnkobW9kZWwuZmlyZSkKcGxvdChtb2RlbC5maXJlKQppbmYgPSBpbmZsdWVuY2VQbG90KG1vZGVsLmZpcmUpCmFtZXNbcm93bmFtZXMoaW5mKSxdCmBgYAoKYGBge3J9Cm1vZGVsID0gbG0obG9nKFNhbGVQcmljZSkgfiBUb3RhbEJzbXRTRiArIEdyTGl2QXJlYSwgZGF0YSA9IGFtZXMpCiAjIENyZWF0ZSBhIHZlY3RvciBvZiBncmFkdWFsbHktY2hhbmdpbmcgY29sb3JzLCB3aXRoIG9uZSBlbnRyeSBmb3IgZWFjaCBkYXRhICMgcG9pbnQKdGhlLmNvbG9ycyA8LSByYWluYm93KG4gPSBucm93KGRmKSkKIyBGb3IgZWFjaCBkYXRhIHBvaW50LCBzZWUgaG93IGl0IHJhbmtzIGFjY29yZGluZyB0byBYMiwgZnJvbSBzbWFsbGVzdCAoMSkKIyB0byBsYXJnZXN0CnRoZS5yYW5rcyA8LSByYW5rKGFtZXMkR3JMaXZBcmVhKQojIFBsb3QgcmVzaWR1YWxzIHZzLiBYMSwgY29sb3JlZCBhY2NvcmRpbmcgdG8gWDIgRGVmaW5pbmcgdGhlIGNvbG9yIGFuZCByYW5rCiMgdmVjdG9ycyBtYWtlcyB0aGlzIG5leHQgbGluZSBhIGJpdCBsZXNzIG15c3RlcmlvdXMsIGJ1dCBpdCdzIG5vdAojIG5lY2Vzc2FyeTsgdGhpcyBjb3VsZCBhbGwgYmUgYSBvbmUtbGluZXIuCnBsb3QoYW1lcyRUb3RhbEJzbXRTRiwgcmVzaWR1YWxzKG1vZGVsKSwgcGNoID0gMTksIGNvbCA9IHRoZS5jb2xvcnNbdGhlLnJhbmtzXSwgeWxhYiA9ICJSZXNpZHVhbHMiKQpgYGAKCmBgYHtyfQpoaXN0KHNxcnQoYW1lcyRUb3RhbEJzbXRTRikpCmhpc3QoYW1lcyRUb3RhbEJzbXRTRikKaGlzdChsb2coYW1lcyRTYWxlUHJpY2UpKQpgYGAKCgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQptb2RlbC50ZXN0ID0gbG0oU2FsZVByaWNlIH4gVG90YWxTRiArIEFmdGVyV1cyLCBkYXRhID0gYW1lcykKcmVzID0gZGF0YS5mcmFtZShjYmluZChhbWVzJEFmdGVyV1cyLCByZXNpZHVhbHMobW9kZWwudGVzdCkpKQpjb2xuYW1lcyhyZXMpID0gYygnQWZ0ZXJXVzInLCdSZXNpZHVhbHMnKQpnZ3Bsb3QoZGF0YSA9IHJlcywgYWVzKHggPSBmYWN0b3IoQWZ0ZXJXVzIpLCB5ID0gUmVzaWR1YWxzKSkgKyBnZW9tX2JveHBsb3QoKQpnZ3Bsb3QoZGF0YSA9IHJlcywgYWVzKHg9UmVzaWR1YWxzKSkgKyBnZW9tX2RlbnNpdHkoYWxwaGE9LjIsIGZpbGw9IiNGRjY2NjYiKSArIGZhY2V0X2dyaWQocmVzJEFmdGVyV1cyLCBzY2FsZXMgPSAnZnJlZV95JykKYGBgCgpgYGB7cn0KYW1lcyAlPiUgCiAgZmlsdGVyKFllYXJCdWlsdCA+IDE5OTApICU+JSAKICBncm91cF9ieShZZWFyQnVpbHQsQ2VudHJhbEFpcikgJT4lIAogIHRhbGx5KCkgJT4lIAogIGdncGxvdChhZXMoeCA9IFllYXJCdWlsdCwgeT1uLCBmaWxsPUNlbnRyYWxBaXIpKSArIGdlb21fY29sKCkKYGBgCmBgYHtyfQphbWVzICU+JSAKICBmaWx0ZXIoWWVhckJ1aWx0ID4gMTk5MCkgJT4lIAogIGdyb3VwX2J5KFllYXJCdWlsdCxNU1N1YkNsYXNzKSAlPiUgCiAgdGFsbHkoKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gWWVhckJ1aWx0LCB5PW4sIGZpbGw9ZmFjdG9yKE1TU3ViQ2xhc3MpKSkgKyBnZW9tX2NvbCgpCmBgYAoKYGBge3J9Cmhpc3QoYW1lcyRTYWxlUHJpY2UpCmBgYApgYGB7cn0KCmBgYAoKCmBgYHtyfQpiID0gYygtSW5mLCAxLjJlNSwgMmU1LDVlNSxJbmYpCm5hbWVzID0gYygnQ2hlYXAnLCdBdmVyYWdlJywnRXhwZW5zaXZlJywnVmVyeSBFeHBlbnNpdmUnKQpiaW5zID0gY3V0KGFtZXMkU2FsZVByaWNlLCBicmVha3MgPSBiLCBsYWJlbHMgPSBuYW1lcykKCmFtZXMgJT4lIAogIGNiaW5kKC4sIGJpbnMpICU+JSAKICBmaWx0ZXIoWWVhckJ1aWx0ID4gMTkzMCkgJT4lIAogIGdyb3VwX2J5KFllYXJCdWlsdCxiaW5zKSAlPiUgCiAgdGFsbHkoKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gWWVhckJ1aWx0LCB5PW4sIGZpbGw9ZmFjdG9yKGJpbnMpKSkgKyBnZW9tX2NvbCgpCgpgYGAKCmBgYHtyfQphbWVzICU+JSAKICBncm91cF9ieShZclNvbGQsWWVhckJ1aWx0KSAlPiUgCiAgdGFsbHkoKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gWXJTb2xkLCB5PW4sIGZpbGw9WWVhckJ1aWx0KSkgKyBnZW9tX2NvbChwb3NpdGlvbj0nZmlsbCcpCmBgYAoKYGBge3J9Cm1vZGVsLnRlc3QgPSBsbShsb2coU2FsZVByaWNlKSB+IHNxcnQoVG90YWxTRikqTVNTdWJDbGFzcyxkYXRhPWFtZXMgKQpzdW1tYXJ5KG1vZGVsLnRlc3QpCmBgYAoKYGBge3J9CmxpYnJhcnkoQXBwbGllZFByZWRpY3RpdmVNb2RlbGluZykKdHJhbnNwYXJlbnRUaGVtZSh0cmFucyA9IC40KQpsaWJyYXJ5KGNhcmV0KQpwbG90U3Vic2V0IDwtIGRhdGEuZnJhbWUoc2NhbGUoYW1lc1ssIGMoIlNhbGVQcmljZSIsICJNU1N1YkNsYXNzIildKSkgCnh5cGxvdChTYWxlUHJpY2UgfiBNU1N1YkNsYXNzLAogICAgICAgZGF0YSA9IGFtZXMsCiAgICAgICBncm91cHMgPSBhbWVzJEFmdGVyV1cyLCAKICAgICAgIGF1dG8ua2V5ID0gbGlzdChjb2x1bW5zID0gMikpIApgYGAKCmBgYHtyfQp0cmFuc2Zvcm1lZCA8LSBzcGF0aWFsU2lnbihwbG90U3Vic2V0KQp0cmFuc2Zvcm1lZCA8LSBhcy5kYXRhLmZyYW1lKHRyYW5zZm9ybWVkKQp4eXBsb3QoU2FsZVByaWNlIH4gTVNTdWJDbGFzcywgCiAgICAgICBkYXRhID0gdHJhbnNmb3JtZWQsIAogICAgICAgZ3JvdXBzID0gYW1lcyRBZnRlcldXMiwgCiAgICAgICBhdXRvLmtleSA9IGxpc3QoY29sdW1ucyA9IDIpKSAKYGBgCgpgYGB7cn0KYW1lc190cmFpbiAlPiUgCiAgZ3JvdXBfYnkoTVNTdWJDbGFzcyxBZnRlcldXMikgJT4lIAogIGdncGxvdChhZXMoeCA9IGZhY3RvcihNU1N1YkNsYXNzKSwgeT1TYWxlUHJpY2UpKSArIGdlb21fYm94cGxvdCgpICtmYWNldF9ncmlkKCgnQWZ0ZXJXVzInKSkKCmFtZXNfdHJhaW4gJT4lIAogIGdyb3VwX2J5KE1TU3ViQ2xhc3MsSG91c2VTdHlsZSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGZhY3RvcihNU1N1YkNsYXNzKSwgeT1TYWxlUHJpY2UpKSArIGdlb21fYm94cGxvdCgpICtmYWNldF93cmFwKCgnSG91c2VTdHlsZScpKQpgYGAKCmBgYHtyfQpkLnRyZWUgPSBycGFydDo6cnBhcnQoZm9ybXVsYSA9IFNhbGVQcmljZSB+IE5laWdoYm9yaG9vZCwgZGF0YSA9IGFtZXMpCnJwYXJ0LnBsb3Q6OnJwYXJ0LnBsb3QoZC50cmVlKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KHRyZWUuYmlucykKbGlicmFyeShycGFydCkKc2FtcGxlLmRmIDwtIGFtZXNbLGMoJ05laWdoYm9yaG9vZCcsJ1NhbGVQcmljZScsJ01TWm9uaW5nJyldCmJpbm5lZC5kZiA8LSB0cmVlLmJpbnMoZGF0YSA9IHNhbXBsZS5kZiwgeSA9IFNhbGVQcmljZSkjLCBiaW4ubm0gPSAiYmluLiIsIHJldHVybiA9ICJuZXcuZmN0cnMiKQp1bmlxdWUoYmlubmVkLmRmJE5laWdoYm9yaG9vZCkjY3VycmVudCBsZXZlbHMgb2YgTmVpZ2hib3Job29kCmBgYAoKYGBge3J9CnNhbXBsZS5kZiA8LSB0cmVlLmJpbnM6OkFtZXNJbXBGY3Ryc1ssIGMoIk5laWdoYm9yaG9vZCIsICJNUy5ab25pbmciLCAiU2FsZVByaWNlIildCmJpbm5lZC5kZiA9IHRyZWUuYmlucyhkYXRhID0gc2FtcGxlLmRmLCB5ID0gU2FsZVByaWNlLCBiaW4ubm0gPSAiYmluLiIsIHJldHVybiA9ICJsa3VwLmxpc3QiKQp1bmlxdWUoYmlubmVkLmRmJE5laWdoYm9yaG9vZCkjY3VycmVudCBsZXZlbHMgb2YgTmVpZ2hib3Job29kCgphbWVzID0gYW1lcyAlPiUgbGVmdF9qb2luKC4sIGJpbm5lZC5kZltbMV1dWyxjKCdOZWlnaGJvcmhvb2QnLCdDYXRlZ29yaWVzJyldLCBieT0nTmVpZ2hib3Job29kJykKYW1lcyA9IGFtZXMgJT4lIGxlZnRfam9pbiguLCBiaW5uZWQuZGZbWzJdXVssYygnTVMuWm9uaW5nJywnQ2F0ZWdvcmllcycpXSwgYnk9YygnTVNab25pbmcnPSdNUy5ab25pbmcnKSkKYGBgCgpgYGB7cn0KCnRlc3RbIWNvbXBsZXRlLmNhc2VzKHRlc3QpLF0Kc3VtKGlzLm5hKHRlc3QpKQpgYGAKCgpgYGB7cn0KI3Rlc3QuYmlubiA9IHRlc3QgJT4lIHNlbGVjdChjKCdOZWlnaGJvcmhvb2QnLCcnKSkKdGVzdC5iaW5uID0ga2tubihQcmljZVJhbmdlIH4gCiAgICAgICAgICAgICAgICAgICBOZWlnaGJvcmhvb2QgKyAKICAgICAgICAgICAgICAgICAgIFRvdGFsQmF0aHJvb21zICsgCiAgICAgICAgICAgICAgICAgICBLaXRjaGVuQWJ2R3IgKwogICAgICAgICAgICAgICAgICAgT3ZlcmFsbFF1YWwgKwogICAgICAgICAgICAgICAgICAgRXh0ZXJRdWFsICsKICAgICAgICAgICAgICAgICAgIE1TWm9uaW5nLCBjb21wbGV0ZSwgbWlzc2luZywgaz0xKQoKCmFtZXNfbm9TYWxlID0gYW1lcyAlPiUgc2VsZWN0KC1TYWxlUHJpY2UpCmFsbCA9IHJiaW5kKGFtZXNfbm9TYWxlLCB0ZXN0KQpgYGAKCmBgYHtyfQppbXB1dGVkX0RhdGEgPC0gbWljZSh0ZXN0LCBtPTUsIG1heGl0ID0gMjAsIG1ldGhvZCA9ICdwbW0nLCBzZWVkID0gNTAwKTsKY29tcGxldGVEYXRhID0gY29tcGxldGUoaW1wdXRlZF9EYXRhLDEpCnRlc3QuY29tcCA9IHRpYmJsZShjb21wbGV0ZURhdGEpCmBgYAoKYGBge3J9CgpgYGAKCmBgYHtyfQp0ZXN0LmNvbXBbJ3ByZWRzJ10gPSBnYW0uYmFzZS5wcmVkaWN0aW9ucwpgYGAKCmBgYHtyfQp0ZXN0LmNvbXAgJT4lIAogIGdncGxvdChhZXMoeCA9IDE6bnJvdyh0ZXN0LmNvbXApLCB5ID0gcHJlZHMsIGNvbCA9IFByaWNlUmFuZ2UpKSArIGdlb21fcG9pbnQoKQpgYGAKCg==